Skip to content

Commit f84b438

Browse files
[EWT-78] Add payment refunds support (#201)
1 parent 8aa282f commit f84b438

21 files changed

+637
-52
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Main properties
22
group=com.truelayer
33
archivesBaseName=truelayer-java
4-
version=6.1.0
4+
version=6.2.0
55

66
# Artifacts properties
77
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/

src/main/java/com/truelayer/java/payments/IPaymentsApi.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.truelayer.java.http.entities.ApiResponse;
66
import com.truelayer.java.payments.entities.*;
77
import com.truelayer.java.payments.entities.paymentdetail.PaymentDetail;
8+
import com.truelayer.java.payments.entities.paymentrefund.PaymentRefund;
89
import java.util.concurrent.CompletableFuture;
910
import retrofit2.http.*;
1011

@@ -91,4 +92,34 @@ CompletableFuture<ApiResponse<AuthorizationFlowResponse>> submitConsent(
9192
@POST("/payments/{id}/authorization-flow/actions/form")
9293
CompletableFuture<ApiResponse<AuthorizationFlowResponse>> submitForm(
9394
@Path("id") String paymentId, @Body SubmitFormRequest request);
95+
96+
/**
97+
* Refund a merchant account payment.
98+
* @param paymentId the payment identifier
99+
* @param request a create refund request payload
100+
* @return the response of the <i>Create Payment Refund</i> operation
101+
* @see <a href="https://docs.truelayer.com/reference/create-payment-refund"><i>Create Payment Refund</i> API reference</a>
102+
*/
103+
@POST("/payments/{id}/refunds")
104+
CompletableFuture<ApiResponse<CreatePaymentRefundResponse>> createPaymentRefund(
105+
@Path("id") String paymentId, @Body CreatePaymentRefundRequest request);
106+
107+
/**
108+
* Returns all refunds of a payment.
109+
* @param paymentId the payment identifier
110+
* @return the response of the <i>Get Payment Refunds</i> operation
111+
* @see <a href="https://docs.truelayer.com/reference/get-payment-refunds"><i>Get Payment Refunds</i> API reference</a>
112+
*/
113+
@GET("/payments/{id}/refunds")
114+
CompletableFuture<ApiResponse<ListPaymentRefundsResponse>> listPaymentRefunds(@Path("id") String paymentId);
115+
116+
/**
117+
* Returns refund details.
118+
* @param paymentId the payment identifier
119+
* @return the response of the <i>Get Payment Refund</i> operation
120+
* @see <a href="https://docs.truelayer.com/reference/get-payment-refund"><i>Get Payment Refund</i> API reference</a>
121+
*/
122+
@GET("/payments/{paymentId}/refunds/{refundId}")
123+
CompletableFuture<ApiResponse<PaymentRefund>> getPaymentRefundById(
124+
@Path("paymentId") String paymentId, @Path("refundId") String refundId);
94125
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.truelayer.java.payments.entities;
2+
3+
import java.util.Map;
4+
import lombok.Builder;
5+
import lombok.EqualsAndHashCode;
6+
import lombok.Getter;
7+
import lombok.ToString;
8+
9+
@Builder
10+
@Getter
11+
@ToString
12+
@EqualsAndHashCode
13+
public class CreatePaymentRefundRequest {
14+
private int amountInMinor;
15+
private String reference;
16+
private Map<String, String> metadata;
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.truelayer.java.payments.entities;
2+
3+
import lombok.EqualsAndHashCode;
4+
import lombok.Getter;
5+
import lombok.ToString;
6+
7+
@ToString
8+
@EqualsAndHashCode
9+
@Getter
10+
public class CreatePaymentRefundResponse {
11+
private String id;
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.truelayer.java.payments.entities;
2+
3+
import com.truelayer.java.payments.entities.paymentrefund.PaymentRefund;
4+
import java.util.List;
5+
import lombok.Value;
6+
7+
@Value
8+
public class ListPaymentRefundsResponse {
9+
List<PaymentRefund> items;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.truelayer.java.payments.entities.paymentrefund;
2+
3+
import lombok.EqualsAndHashCode;
4+
import lombok.Value;
5+
6+
@Value
7+
@EqualsAndHashCode(callSuper = false)
8+
public class AuthorizedPaymentRefund extends PaymentRefund {
9+
Status status = Status.AUTHORIZED;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.truelayer.java.payments.entities.paymentrefund;
2+
3+
import java.time.ZonedDateTime;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Value;
6+
7+
@Value
8+
@EqualsAndHashCode(callSuper = false)
9+
public class ExecutedPaymentRefund extends PaymentRefund {
10+
Status status = Status.EXECUTED;
11+
ZonedDateTime executedAt;
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.truelayer.java.payments.entities.paymentrefund;
2+
3+
import java.time.ZonedDateTime;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Value;
6+
7+
@Value
8+
@EqualsAndHashCode(callSuper = false)
9+
public class FailedPaymentRefund extends PaymentRefund {
10+
Status status = Status.FAILED;
11+
ZonedDateTime failedAt;
12+
String failureReason;
13+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.truelayer.java.payments.entities.paymentrefund;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
import com.fasterxml.jackson.annotation.JsonSubTypes;
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.truelayer.java.TrueLayerException;
7+
import com.truelayer.java.entities.CurrencyCode;
8+
import java.time.ZonedDateTime;
9+
import java.util.Map;
10+
import lombok.Getter;
11+
12+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "status", defaultImpl = PendingPaymentRefund.class)
13+
@JsonSubTypes({
14+
@JsonSubTypes.Type(value = PendingPaymentRefund.class, name = "pending"),
15+
@JsonSubTypes.Type(value = AuthorizedPaymentRefund.class, name = "authorized"),
16+
@JsonSubTypes.Type(value = ExecutedPaymentRefund.class, name = "executed"),
17+
@JsonSubTypes.Type(value = FailedPaymentRefund.class, name = "failed"),
18+
})
19+
@Getter
20+
public abstract class PaymentRefund {
21+
private String id;
22+
private int amountInMinor;
23+
private CurrencyCode currency;
24+
private String reference;
25+
private Map<String, String> metadata;
26+
private ZonedDateTime createdAt;
27+
28+
@JsonIgnore
29+
public abstract Status getStatus();
30+
31+
@JsonIgnore
32+
public boolean isPending() {
33+
return this instanceof PendingPaymentRefund;
34+
}
35+
36+
@JsonIgnore
37+
public boolean isAuthorized() {
38+
return this instanceof AuthorizedPaymentRefund;
39+
}
40+
41+
@JsonIgnore
42+
public boolean isExecuted() {
43+
return this instanceof ExecutedPaymentRefund;
44+
}
45+
46+
@JsonIgnore
47+
public boolean isFailed() {
48+
return this instanceof FailedPaymentRefund;
49+
}
50+
51+
@JsonIgnore
52+
public PendingPaymentRefund asPendingPaymentRefund() {
53+
if (!isPending()) throw new TrueLayerException(buildErrorMessage());
54+
55+
return (PendingPaymentRefund) this;
56+
}
57+
58+
@JsonIgnore
59+
public AuthorizedPaymentRefund asAuthorizedPaymentRefund() {
60+
if (!isAuthorized()) throw new TrueLayerException(buildErrorMessage());
61+
62+
return (AuthorizedPaymentRefund) this;
63+
}
64+
65+
@JsonIgnore
66+
public ExecutedPaymentRefund asExecutedPaymentRefund() {
67+
if (!isExecuted()) throw new TrueLayerException(buildErrorMessage());
68+
69+
return (ExecutedPaymentRefund) this;
70+
}
71+
72+
@JsonIgnore
73+
public FailedPaymentRefund asFailedPaymentRefund() {
74+
if (!isFailed()) throw new TrueLayerException(buildErrorMessage());
75+
76+
return (FailedPaymentRefund) this;
77+
}
78+
79+
private String buildErrorMessage() {
80+
return String.format("Payment refund is of type %s.", this.getClass().getSimpleName());
81+
}
82+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.truelayer.java.payments.entities.paymentrefund;
2+
3+
import lombok.EqualsAndHashCode;
4+
import lombok.Value;
5+
6+
@Value
7+
@EqualsAndHashCode(callSuper = false)
8+
public class PendingPaymentRefund extends PaymentRefund {
9+
Status status = Status.PENDING;
10+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.truelayer.java.payments.entities.paymentrefund;
2+
3+
import com.fasterxml.jackson.annotation.JsonValue;
4+
import lombok.Getter;
5+
import lombok.RequiredArgsConstructor;
6+
7+
@RequiredArgsConstructor
8+
@Getter
9+
public enum Status {
10+
PENDING("pending"),
11+
AUTHORIZED("authorized"),
12+
EXECUTED("executed"),
13+
FAILED("failed");
14+
15+
@JsonValue
16+
private final String status;
17+
}

src/test/java/com/truelayer/java/acceptance/MandatesAcceptanceTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ private void authorizeMandate(AuthorizationFlowResponse authorizationFlowRespons
358358
// first we check the state of the mandate returned by the gateway
359359
assertTrue(
360360
authorizationFlowResponse.isAuthorizing(),
361-
"Mandate status is" + authorizationFlowResponse.getStatus().getStatus());
361+
"Mandate status is " + authorizationFlowResponse.getStatus().getStatus());
362362

363363
// follow the redirect uri and parse its Location HTTP response header
364364
URI redirectUri = authorizationFlowResponse

0 commit comments

Comments
 (0)