package uk.gov.pay.api.utils.mocks; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import com.github.tomakehurst.wiremock.matching.StringValuePattern; import com.google.common.collect.ImmutableMap; import com.google.gson.GsonBuilder; import uk.gov.pay.api.model.DirectDebitPaymentState; import uk.gov.pay.api.model.directdebit.DirectDebitConnectorPaymentResponse; import uk.gov.pay.api.model.directdebit.mandates.MandateConnectorRequest; import uk.gov.pay.api.model.directdebit.mandates.MandateState; import uk.gov.pay.api.model.search.directdebit.MandateSearchConnectorResponse; import uk.gov.pay.api.utils.JsonStringBuilder; import uk.gov.pay.commons.model.ErrorIdentifier; import java.util.List; import java.util.Map; import java.util.Optional; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.lang.String.format; import static java.util.Arrays.asList; import static javax.ws.rs.core.HttpHeaders.ACCEPT; import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; import static javax.ws.rs.core.HttpHeaders.LOCATION; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.eclipse.jetty.http.HttpStatus.BAD_REQUEST_400; import static org.eclipse.jetty.http.HttpStatus.CREATED_201; import static org.eclipse.jetty.http.HttpStatus.FORBIDDEN_403; import static org.eclipse.jetty.http.HttpStatus.OK_200; import static uk.gov.pay.commons.model.ErrorIdentifier.GENERIC; import static uk.gov.pay.commons.model.ErrorIdentifier.GO_CARDLESS_ACCOUNT_NOT_LINKED; public class ConnectorDDMockClient extends BaseConnectorMockClient { private static final String SEARCH_PAYMENTS_PATH = "/v1/api/accounts/%s/payments"; static String CONNECTOR_MOCK_MANDATES_PATH = CONNECTOR_MOCK_ACCOUNTS_PATH + "/mandates"; static String CONNECTOR_MOCK_MANDATE_PATH = CONNECTOR_MOCK_MANDATES_PATH + "/%s"; public ConnectorDDMockClient(WireMockClassRule wireMockClassRule) { super(wireMockClassRule); } public void respondWithChargeFound( //TODO use builders String mandateId, long amount, String gatewayAccountId, String paymentId, DirectDebitPaymentState state, String returnUrl, String description, String reference, String paymentProvider, String createdDate, String chargeTokenId) { String chargeResponseBody = buildPaymentRequestResponse(mandateId, amount, paymentId, state, returnUrl, description, reference, paymentProvider, createdDate, validGetLink(chargeLocation(gatewayAccountId, paymentId), "self"), validGetLink(nextUrl(paymentId), "next_url"), validPostLink(nextUrlPost(), "next_url_post", "application/x-www-form-urlencoded", Map.of("chargeTokenId", chargeTokenId))); whenGetCharge(gatewayAccountId, paymentId, aResponse() .withStatus(OK_200) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withBody(chargeResponseBody)); } public void respondWithPaymentCreated(DirectDebitConnectorPaymentResponse response, String gatewayAccountId) throws JsonProcessingException { var body = new ObjectMapper().writeValueAsString(response); var responseEnclosed = new ResponseDefinitionBuilder() .withStatus(201) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withBody(body); wireMockClassRule .stubFor(post(urlPathEqualTo(format("/v1/api/accounts/%s/charges/collect", gatewayAccountId))) .willReturn(responseEnclosed)); } public void respondOk_whenCreateMandateRequest(CreateMandateRequestParams params) { setupCreateMandate(params.getGatewayAccountId(), aResponse() .withStatus(CREATED_201) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withHeader(LOCATION, format("/v1/api/accounts/%s/mandates/%s", params.getGatewayAccountId(), params.getMandateId())) .withBody(buildCreateMandateResponse( params.getMandateId(), params.getServiceReference(), params.getReturnUrl(), params.getCreatedDate(), params.getState(), params.getDescription(), validGetLink(mandateLocation(params.getGatewayAccountId(), params.getMandateId()), "self"), validGetLink(nextUrl(params.getChargeTokenId()), "next_url"), validPostLink(nextUrlPost(), "next_url_post", "application/x-www-form-urlencoded", Map.of("chargeTokenId", params.getChargeTokenId()) ))) ); } public void respondOk_whenGetMandateRequest(DDConnectorResponseToGetMandateParams params) { setupGetMandate(params.getMandateId(), params.getGatewayAccountId(), aResponse() .withStatus(200) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withBody(buildGetMandateResponse(params))); } public void respondOk_whenSearchMandatesRequest(Map<String, StringValuePattern> searchParams, MandateSearchConnectorResponse response, String gatewayAccountId) throws JsonProcessingException { String responseAsJson = new ObjectMapper().writeValueAsString(response); wireMockClassRule.stubFor(get(urlPathEqualTo(format(CONNECTOR_MOCK_MANDATES_PATH, gatewayAccountId))) .withHeader(ACCEPT, matching(APPLICATION_JSON)) .withQueryParams(searchParams) .willReturn(aResponse() .withStatus(200) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withBody(responseAsJson))); } public void respondBadRequest_whenCreateAgreementRequest(String gatewayAccountId, String errorMsg) { setupCreateMandate(gatewayAccountId, withStatusAndErrorMessage(BAD_REQUEST_400, errorMsg, GENERIC)); } public void respondWithGCAccountNotLinked_whenCreateMandateRequest(String gatewayAccountId, String errorMsg) { setupCreateMandate(gatewayAccountId, withStatusAndErrorMessage(FORBIDDEN_403, errorMsg, GO_CARDLESS_ACCOUNT_NOT_LINKED)); } public void respondOk_whenSearchPayments(String gatewayAccountId, String response) { whenSearchPayments(gatewayAccountId, aResponse() .withStatus(OK_200) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withBody(response)); } private String buildCreateMandateResponse(String mandateId, String serviceReference, String returnUrl, String createdDate, MandateState state, String description, ImmutableMap<?, ?>... links) { JsonStringBuilder builder = new JsonStringBuilder() .add("mandate_id", mandateId) .add("service_reference", serviceReference) .add("return_url", returnUrl) .add("payment_provider", "gocardless") .add("created_date", createdDate) .add("state", state) .add("links", asList(links)); Optional.ofNullable(description).ifPresent(x -> builder.add("description", description)); return builder.build(); } private String buildGetMandateResponse(DDConnectorResponseToGetMandateParams params) { var links = List.of(validGetLink(mandateLocation(params.getGatewayAccountId(), params.getMandateId()), "self"), validGetLink(nextUrl(params.getChargeTokenId()), "next_url"), validPostLink(nextUrlPost(), "next_url_post", "application/x-www-form-urlencoded", Map.of("chargeTokenId", params.getChargeTokenId()))); return gson.toJson(Map.of("mandate_id", params.getMandateId(), "mandate_reference", params.getMandateReference(), "return_url", params.getReturnUrl(), "service_reference", params.getServiceReference(), "state", params.getState(), "provider_id", params.getProviderId(), "created_date", params.getCreatedDate(), "payer", params.getPayer(), "links", links)); } private String buildPaymentRequestResponse(String mandateId, long amount, String chargeId, DirectDebitPaymentState state, String returnUrl, String description, String reference, String paymentProvider, String createdDate, ImmutableMap<?, ?>... links) { JsonStringBuilder jsonStringBuilder = new JsonStringBuilder() .add("payment_id", chargeId) .add("mandate_id", mandateId) .add("amount", amount) .add("reference", reference) .add("description", description) .add("state", state) .add("return_url", returnUrl) .add("payment_provider", paymentProvider) .add("created_date", createdDate) .add("links", asList(links)); return jsonStringBuilder.build(); } @Override String nextUrlPost() { return "http://frontend_direct_debit/secure/"; } private void setupCreateMandate(String gatewayAccountId, ResponseDefinitionBuilder response) { wireMockClassRule.stubFor(post(urlPathEqualTo(format("/v1/api/accounts/%s/mandates", gatewayAccountId))) .withHeader(CONTENT_TYPE, matching(APPLICATION_JSON)).willReturn(response)); } private void setupGetMandate(String mandateId, String gatewayAccountId, ResponseDefinitionBuilder response) { wireMockClassRule.stubFor(get(urlPathEqualTo(format("/v1/api/accounts/%s/mandates/%s", gatewayAccountId, mandateId))) .willReturn(response)); } private ResponseDefinitionBuilder withStatusAndErrorMessage(int statusCode, String errorMsg, ErrorIdentifier errorIdentifier) { return aResponse() .withStatus(statusCode) .withHeader(CONTENT_TYPE, APPLICATION_JSON) .withBody(new GsonBuilder().create().toJson(Map.of( "message", List.of(errorMsg), "error_identifier", errorIdentifier.toString()))); } public void verifyCreateMandateConnectorRequest(MandateConnectorRequest mandateConnectorRequest, String gatewayAccountId) throws JsonProcessingException { wireMockClassRule.verify(1, postRequestedFor(urlEqualTo(format("/v1/api/accounts/%s/mandates", gatewayAccountId))) .withRequestBody(equalToJson(objectMapper.writeValueAsString(mandateConnectorRequest), true, true))); } private void whenSearchPayments(String gatewayAccountId, ResponseDefinitionBuilder response) { wireMockClassRule.stubFor(get(urlPathEqualTo(format(SEARCH_PAYMENTS_PATH, gatewayAccountId))) .withHeader(ACCEPT, matching(APPLICATION_JSON)).willReturn(response)); } private String mandateLocation(String accountId, String mandateId) { return format(CONNECTOR_MOCK_MANDATE_PATH, accountId, mandateId); } }