Skip to content

Commit

Permalink
Merge pull request #2521 from alphagov/pp_7077_get_refunds_from_ledger
Browse files Browse the repository at this point in the history
PP-7077 Get refunds from Ledger
  • Loading branch information
kbottla authored Sep 4, 2020
2 parents 1d54c9c + ad2a519 commit 72dbd5f
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package uk.gov.pay.connector.paritycheck;

import javax.ws.rs.core.Response;

public class GetRefundsForPaymentException extends LedgerException {
public GetRefundsForPaymentException(Response response) {
super(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package uk.gov.pay.connector.paritycheck;

import javax.ws.rs.core.Response;

public class LedgerException extends RuntimeException {
private Integer status;

public LedgerException(Response response) {
super(response.toString());
status = response.getStatus();
}

public LedgerException(Exception exception) {
super(exception);
}

@Override
public String toString() {
return "LedgerException{" +
"status=" + status +
", message=" + getMessage() +
'}';
}
}
47 changes: 42 additions & 5 deletions src/main/java/uk/gov/pay/connector/paritycheck/LedgerService.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package uk.gov.pay.connector.paritycheck;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.gov.pay.connector.app.ConnectorConfiguration;

import javax.inject.Inject;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.Optional;

import static java.lang.String.format;
import static net.logstash.logback.argument.StructuredArguments.kv;
import static org.apache.http.HttpStatus.SC_OK;
import static uk.gov.pay.logging.LoggingKeys.GATEWAY_ACCOUNT_ID;
import static uk.gov.pay.logging.LoggingKeys.PAYMENT_EXTERNAL_ID;

public class LedgerService {

private final Logger logger = LoggerFactory.getLogger(LedgerService.class);

private final Client client;
private final String ledgerUrl;

Expand Down Expand Up @@ -51,12 +59,33 @@ public Optional<LedgerTransaction> getTransactionForGatewayAccount(String id, Lo
return getTransactionFromLedger(uri);
}

public RefundTransactionsForPayment getRefundsForPayment(Long gatewayAccountId, String paymentExternalId) {
var uri = UriBuilder
.fromPath(ledgerUrl)
.path(format("/v1/transaction/%s/transaction", paymentExternalId))
.queryParam("gateway_account_id", gatewayAccountId);

Response response = getResponse(uri);

if (response.getStatus() == SC_OK) {
try {
return response.readEntity(RefundTransactionsForPayment.class);
} catch (ProcessingException exception) {
logger.error("Error processing response from ledger for payment refunds: {} {}",
kv(GATEWAY_ACCOUNT_ID, gatewayAccountId),
kv(PAYMENT_EXTERNAL_ID, paymentExternalId));
throw new LedgerException(exception);
}
} else {
logger.error("Received non-success status code for get refunds for payment from Ledger: {}, {}",
kv(GATEWAY_ACCOUNT_ID, gatewayAccountId),
kv(PAYMENT_EXTERNAL_ID, paymentExternalId));
throw new GetRefundsForPaymentException(response);
}
}

private Optional<LedgerTransaction> getTransactionFromLedger(UriBuilder uri) {
Response response = client
.target(uri)
.request()
.accept(MediaType.APPLICATION_JSON)
.get();
Response response = getResponse(uri);

if (response.getStatus() == SC_OK) {
return Optional.of(response.readEntity(LedgerTransaction.class));
Expand All @@ -65,4 +94,12 @@ private Optional<LedgerTransaction> getTransactionFromLedger(UriBuilder uri) {
return Optional.empty();
}

private Response getResponse(UriBuilder uri) {
return client
.target(uri)
.request()
.accept(MediaType.APPLICATION_JSON)
.get();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package uk.gov.pay.connector.paritycheck;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

import java.util.List;

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public class RefundTransactionsForPayment {

String parentTransactionId;
List<LedgerTransaction> transactions;

public String getParentTransactionId() {
return parentTransactionId;
}

public List<LedgerTransaction> getTransactions() {
return transactions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,57 @@
import org.mockito.junit.MockitoJUnitRunner;
import uk.gov.pay.connector.app.ConnectorConfiguration;

import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.List;
import java.util.Optional;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
import static org.apache.http.HttpStatus.SC_OK;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static uk.gov.pay.connector.util.TestTemplateResourceLoader.LEDGER_GET_REFUNDS_FOR_PAYMENT;
import static uk.gov.pay.connector.util.TestTemplateResourceLoader.LEDGER_GET_TRANSACTION;
import static uk.gov.pay.connector.util.TestTemplateResourceLoader.load;

@RunWith(MockitoJUnitRunner.class)
public class LedgerServiceTest {

ObjectMapper objectMapper = new ObjectMapper();
private ObjectMapper objectMapper = new ObjectMapper();
private LedgerService ledgerService;
private Response mockResponse;

@Before
public void setUp() throws JsonProcessingException {
public void setUp() {
Client mockClient = mock(Client.class);
ConnectorConfiguration mockConnectorConfiguration = mock(ConnectorConfiguration.class);
WebTarget mockWebTarget = mock(WebTarget.class);
Invocation.Builder mockBuilder = mock(Invocation.Builder.class);
Response mockResponse = mock(Response.class);
mockResponse = mock(Response.class);

when(mockConnectorConfiguration.getLedgerBaseUrl()).thenReturn("http://ledgerUrl");
when(mockClient.target(any(UriBuilder.class))).thenReturn(mockWebTarget);
when(mockWebTarget.request()).thenReturn(mockBuilder);
when(mockBuilder.accept(APPLICATION_JSON)).thenReturn(mockBuilder);
when(mockBuilder.get()).thenReturn(mockResponse);

when(mockResponse.readEntity(LedgerTransaction.class)).thenReturn(objectMapper.readValue(load(LEDGER_GET_TRANSACTION), LedgerTransaction.class));
when(mockResponse.getStatus()).thenReturn(SC_OK);
ledgerService = new LedgerService(mockClient, mockConnectorConfiguration);
}

@Test
public void getTransaction_shouldSerialiseLedgerTransaction() {
public void getTransaction_shouldSerialiseLedgerTransaction() throws JsonProcessingException {
when(mockResponse.readEntity(LedgerTransaction.class)).thenReturn(objectMapper.readValue(load(LEDGER_GET_TRANSACTION), LedgerTransaction.class));

String externalId = "external-id";
Optional<LedgerTransaction> mayBeTransaction = ledgerService.getTransaction("external-id");

Expand All @@ -63,4 +69,43 @@ public void getTransaction_shouldSerialiseLedgerTransaction() {
assertThat(transaction.getGatewayAccountId(), is("3"));
assertThat(transaction.getExternalMetaData(), is(notNullValue()));
}

@Test
public void getRefundsFromLedgerShouldSerialiseResponseCorrectly() throws JsonProcessingException {
when(mockResponse.readEntity(RefundTransactionsForPayment.class)).
thenReturn(objectMapper.readValue(load(LEDGER_GET_REFUNDS_FOR_PAYMENT), RefundTransactionsForPayment.class));

String externalId = "650516the13q5jpfo435f1m1fm";
RefundTransactionsForPayment refundTransactionsForPayment =
ledgerService.getRefundsForPayment(152L, externalId);

assertThat(refundTransactionsForPayment.getParentTransactionId(), is(externalId));

List<LedgerTransaction> transactions = refundTransactionsForPayment.getTransactions();

assertThat(transactions.get(0).getTransactionId(), is("nklfm1pk9flpu91j815kp2835o"));
assertThat(transactions.get(0).getGatewayAccountId(), is("152"));
assertThat(transactions.get(0).getAmount(), is(100L));
assertThat(transactions.get(0).getState().getStatus(), is("success"));

assertThat(transactions.get(1).getTransactionId(), is("migtkmlt6gvm16sim5h0p7oeje"));
assertThat(transactions.get(1).getAmount(), is(110L));
assertThat(transactions.get(1).getGatewayAccountId(), is("152"));
assertThat(transactions.get(1).getState().getStatus(), is("failed"));
}

@Test(expected = GetRefundsForPaymentException.class)
public void getRefundsFromLedgerShouldThrowExceptionForNon2xxResponse() {
when(mockResponse.getStatus()).thenReturn(SC_NOT_FOUND);

ledgerService.getRefundsForPayment(152L, "external-id");
}

@Test(expected = LedgerException.class)
public void getRefundsFromLedgerShouldThrowExceptionIfResponseCannotBeProcessed() {
when(mockResponse.readEntity(RefundTransactionsForPayment.class)).
thenThrow(ProcessingException.class);

ledgerService.getRefundsForPayment(152L, "external-id");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public class TestTemplateResourceLoader {
public static final String SQS_ERROR_RESPONSE = TEMPLATE_BASE_NAME + "/sqs/error-response.xml";

public static final String LEDGER_GET_TRANSACTION = TEMPLATE_BASE_NAME + "/ledger/transaction.json";
public static final String LEDGER_GET_REFUNDS_FOR_PAYMENT = TEMPLATE_BASE_NAME + "/ledger/refunds_for_payment.json";

public static String load(String location) {
return fixture(location);
Expand Down
62 changes: 62 additions & 0 deletions src/test/resources/templates/ledger/refunds_for_payment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"parent_transaction_id": "650516the13q5jpfo435f1m1fm",
"transactions": [
{
"gateway_account_id": "152",
"amount": 100,
"state": {
"finished": true,
"status": "success"
},
"created_date": "2019-12-23T15:24:07.061Z",
"gateway_transaction_id": "ff3f12da-fa4e-4f2c-8460-fa80d20b3e97",
"refunded_by": "refunded_by_user1",
"transaction_type": "REFUND",
"payment_details": {
"description": "An example payment description",
"reference": "V58CDG66T2",
"email": "[email protected]",
"card_details": {
"cardholder_name": "test",
"card_brand": "Visa",
"last_digits_card_number": "4242",
"first_digits_card_number": "424242",
"expiry_date": "11/21",
"card_type": "credit"
},
"transaction_type": "PAYMENT"
},
"transaction_id": "nklfm1pk9flpu91j815kp2835o",
"parent_transaction_id": "650516the13q5jpfo435f1m1fm"
},
{
"gateway_account_id": "152",
"amount": 110,
"state": {
"finished": true,
"status": "failed"
},
"created_date": "2019-12-23T16:20:12.343Z",
"gateway_transaction_id": "0ecb93b8-2db6-41b1-a63d-5438ab7cf0ba",
"refunded_by": "refunded_by_user1",
"refunded_by_user_email": "[email protected]",
"transaction_type": "REFUND",
"payment_details": {
"description": "An example payment description",
"reference": "V58CDG66T2",
"email": "[email protected]",
"card_details": {
"cardholder_name": "test",
"card_brand": "Visa",
"last_digits_card_number": "4242",
"first_digits_card_number": "424242",
"expiry_date": "11/21",
"card_type": "credit"
},
"transaction_type": "PAYMENT"
},
"transaction_id": "migtkmlt6gvm16sim5h0p7oeje",
"parent_transaction_id": "650516the13q5jpfo435f1m1fm"
}
]
}

0 comments on commit 72dbd5f

Please sign in to comment.