diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/feesandpay/FeePaymentEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/feesandpay/FeePaymentEntity.java new file mode 100644 index 0000000000..18bf49d39a --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/feesandpay/FeePaymentEntity.java @@ -0,0 +1,67 @@ +package uk.gov.hmcts.reform.pcs.ccd.entity.feesandpay; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import uk.gov.hmcts.reform.pcs.ccd.entity.ClaimEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; +import uk.gov.hmcts.reform.pcs.feesandpay.model.PaymentStatus; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +import static jakarta.persistence.FetchType.LAZY; + +@Entity +@Builder +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "fee_payment") +public class FeePaymentEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "claim_id", nullable = false) + @JsonBackReference + private ClaimEntity claim; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "party_id", nullable = false) + @JsonBackReference + private PartyEntity party; + + @CreationTimestamp + @Column(updatable = false, nullable = false) + private LocalDateTime requestDate; + + // Service Request Reference from the createRequest + private String requestReference; + + private String externalReference; + + private BigDecimal amount; + + private String paymentStatus; + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/repository/feeandpay/FeePaymentRepository.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/repository/feeandpay/FeePaymentRepository.java new file mode 100644 index 0000000000..9edfdf66b0 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/repository/feeandpay/FeePaymentRepository.java @@ -0,0 +1,13 @@ +package uk.gov.hmcts.reform.pcs.ccd.repository.feeandpay; + +import org.springframework.data.jpa.repository.JpaRepository; +import uk.gov.hmcts.reform.pcs.ccd.entity.feesandpay.FeePaymentEntity; + +import java.util.Optional; +import java.util.UUID; + +public interface FeePaymentRepository extends JpaRepository { + + Optional findByRequestReference(String requestReference); + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/testcasesupport/TestSupportEnvironment.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/testcasesupport/TestSupportEnvironment.java index 908442db38..8dea559fa2 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/testcasesupport/TestSupportEnvironment.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/testcasesupport/TestSupportEnvironment.java @@ -32,5 +32,6 @@ private static boolean isStubEnvironment(String value) { String lower = value.toLowerCase(Locale.UK); return lower.contains("dev") || lower.contains("preview") || lower.contains("aat"); } + } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/endpoint/PaymentCallBackController.java b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/endpoint/PaymentCallBackController.java new file mode 100644 index 0000000000..027ba0e277 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/endpoint/PaymentCallBackController.java @@ -0,0 +1,32 @@ +package uk.gov.hmcts.reform.pcs.feesandpay.endpoint; + +import io.swagger.v3.oas.annotations.Operation; +import lombok.AllArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import uk.gov.hmcts.reform.pcs.feesandpay.model.ServiceRequestUpdate; +import uk.gov.hmcts.reform.pcs.feesandpay.service.PaymentService; + +import static com.azure.core.http.ContentType.APPLICATION_JSON; + +@RestController +@AllArgsConstructor +public class PaymentCallBackController { + + private final PaymentService paymentService; + + @PostMapping(path = "/service-request-update", consumes = APPLICATION_JSON) + @Operation(description = "Callback to create Fee and Pay service request") + public void ccdSubmitted( + @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorisation, + @RequestHeader(value = "ServiceAuthorization", required = false) String s2sToken, + @RequestBody ServiceRequestUpdate serviceRequestUpdate) { + + paymentService.processPaymentResponse(serviceRequestUpdate); + + } + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/Payment.java b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/Payment.java new file mode 100644 index 0000000000..4e95c491d6 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/Payment.java @@ -0,0 +1,28 @@ +package uk.gov.hmcts.reform.pcs.feesandpay.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Builder +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class Payment { + + @JsonProperty("payment_amount") + private BigDecimal paymentAmount; + @JsonProperty("payment_reference") + private String paymentReference; + @JsonProperty("payment_method") + private String paymentMethod; + @JsonProperty("case_reference") + private String caseReference; + @JsonProperty("account_number") + private String accountNumber; + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/PaymentStatus.java b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/PaymentStatus.java new file mode 100644 index 0000000000..476bb34319 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/PaymentStatus.java @@ -0,0 +1,26 @@ +package uk.gov.hmcts.reform.pcs.feesandpay.model; + +import lombok.Getter; + +@Getter +public enum PaymentStatus { + + PAID("Paid"), + NOT_PAID("Not paid"), + PARTIALLY_PAID("Partially paid"); + + private String value; + + public static PaymentStatus fromValue(String value) { + for (PaymentStatus status : values()) { + if (status.value.equalsIgnoreCase(value)) { + return status; + } + } + throw new IllegalArgumentException("Unknown PaymentStatus value: " + value); + } + + PaymentStatus(String s) { + value = s; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/ServiceRequestUpdate.java b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/ServiceRequestUpdate.java new file mode 100644 index 0000000000..4f4d2e0eea --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/model/ServiceRequestUpdate.java @@ -0,0 +1,28 @@ +package uk.gov.hmcts.reform.pcs.feesandpay.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Builder +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class ServiceRequestUpdate { + + @JsonProperty("service_request_reference") + private String serviceRequestReference; + @JsonProperty("ccd_case_number") + private String ccdCaseNumber; + @JsonProperty("service_request_amount") + private BigDecimal serviceRequestAmount; + @JsonProperty("service_request_status") + private String serviceRequestStatus; + @JsonProperty("payment") + private Payment payment; + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentService.java b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentService.java index fb50931a63..9d48dcc144 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentService.java @@ -1,5 +1,6 @@ package uk.gov.hmcts.reform.pcs.feesandpay.service; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -9,10 +10,21 @@ import uk.gov.hmcts.reform.payments.client.models.FeeDto; import uk.gov.hmcts.reform.payments.request.CreateServiceRequestDTO; import uk.gov.hmcts.reform.payments.response.PaymentServiceResponse; +import uk.gov.hmcts.reform.pcs.ccd.entity.ClaimEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.feesandpay.FeePaymentEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyEntity; +import uk.gov.hmcts.reform.pcs.ccd.repository.feeandpay.FeePaymentRepository; +import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; import uk.gov.hmcts.reform.pcs.feesandpay.mapper.PaymentRequestMapper; import uk.gov.hmcts.reform.pcs.feesandpay.model.FeeDetails; +import uk.gov.hmcts.reform.pcs.feesandpay.model.PaymentStatus; +import uk.gov.hmcts.reform.pcs.feesandpay.model.ServiceRequestUpdate; import uk.gov.hmcts.reform.pcs.idam.IdamService; +import java.time.LocalDateTime; +import java.util.Optional; + @Slf4j @Service @RequiredArgsConstructor @@ -21,6 +33,8 @@ public class PaymentService { private final PaymentsClient paymentsClient; private final PaymentRequestMapper paymentRequestMapper; private final IdamService idamService; + private final FeePaymentRepository feePaymentRepository; + private final PcsCaseService pcsCaseService; @Value("${payments.api.callback-url}") private String callbackUrl; @@ -44,17 +58,12 @@ public class PaymentService { * @param responsibleParty the party responsible for the payment * @return {@link PaymentServiceResponse} containing the service request reference */ - public PaymentServiceResponse createServiceRequest( - String caseReference, - String ccdCaseNumber, - FeeDetails feeDetails, - int volume, - String responsibleParty - ) { - FeeDto feeDto = paymentRequestMapper.toFeeDto(feeDetails, volume); + @Transactional + public PaymentServiceResponse createServiceRequest(String caseReference, String ccdCaseNumber, + FeeDetails feeDetails, int volume, String responsibleParty) { - CasePaymentRequestDto casePaymentRequest = - paymentRequestMapper.toCasePaymentRequest(responsibleParty); + FeeDto feeDto = paymentRequestMapper.toFeeDto(feeDetails, volume); + CasePaymentRequestDto casePaymentRequest = paymentRequestMapper.toCasePaymentRequest(responsibleParty); CreateServiceRequestDTO requestDto = CreateServiceRequestDTO.builder() .callBackUrl(callbackUrl) @@ -65,9 +74,51 @@ public PaymentServiceResponse createServiceRequest( .hmctsOrgId(hmctsOrgId) .build(); - return paymentsClient.createServiceRequest( - idamService.getSystemUserAuthorisation(), - requestDto - ); + PaymentServiceResponse paymentServiceResponse = paymentsClient.createServiceRequest( + idamService.getSystemUserAuthorisation(), requestDto); + + ClaimEntity claimEntity = retrieveClaimEntity(caseReference); + ClaimPartyEntity claimPartyEntity = retrieveClaimPartyEntity(claimEntity, responsibleParty); + saveNewFeePayment(claimEntity, claimPartyEntity, feeDto, paymentServiceResponse.getServiceRequestReference()); + + return paymentServiceResponse; + } + + public void processPaymentResponse(ServiceRequestUpdate serviceRequestUpdate) { + log.info("ServiceRequestUpdate status: {}", serviceRequestUpdate.getServiceRequestStatus()); + Optional byCaseReference = feePaymentRepository + .findByRequestReference(serviceRequestUpdate.getServiceRequestReference()); + if (byCaseReference.isPresent()) { + FeePaymentEntity feePaymentEntity = byCaseReference.get(); + feePaymentEntity.setPaymentStatus(serviceRequestUpdate.getServiceRequestStatus()); + feePaymentRepository.save(feePaymentEntity); + } } + + private ClaimPartyEntity retrieveClaimPartyEntity(ClaimEntity claimEntity, String responsibleParty) { + return claimEntity.getClaimParties() + .stream() + .filter(party -> party.getParty().getOrgName().equals(responsibleParty)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Matching PartyEntity not found")); + } + + private void saveNewFeePayment(ClaimEntity claimEntity, ClaimPartyEntity claimParty, FeeDto feeDto, + String serviceRequestReference) { + FeePaymentEntity feePaymentEntity = FeePaymentEntity.builder() + .claim(claimEntity) + .requestDate(LocalDateTime.now()) + .requestReference(serviceRequestReference) + .amount(feeDto.getCalculatedAmount()) + .party(claimParty.getParty()) + .build(); + feePaymentRepository.save(feePaymentEntity); + } + + private ClaimEntity retrieveClaimEntity(String caseReference) { + PcsCaseEntity pcsCaseEntity = pcsCaseService.loadCase(Long.parseLong(caseReference)); + // Assuming 1 claim per PcsCase + return pcsCaseEntity.getClaims().getFirst(); + } + } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/task/FeesAndPayTaskComponent.java b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/task/FeesAndPayTaskComponent.java index 2feae00d32..f301ef0697 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/task/FeesAndPayTaskComponent.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/feesandpay/task/FeesAndPayTaskComponent.java @@ -83,4 +83,5 @@ public CustomTask feePaymentTask() { } }); } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index f2bdbaa71a..4c00cd8e71 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -188,12 +188,12 @@ access-code: payments: api: - url: ${PAYMENT_API_URL:http://localhost:8083} - callback-url: ${PAY_CALLBACK_URL:http://host.docker.internal:8096/service-request-update} + url: ${PAYMENT_API_URL:http://localhost:8083/payments} + callback-url: ${PAY_CALLBACK_URL:http://host.docker.internal:3206/service-request-update} params: organisationUrn: Mortgage and Landlord Possession Claims hmctsOrgId: ${hmcts.hmctsOrgId} core_case_data: api: - url: ${CCD_DATA_STORE_URL:localhost:4452} \ No newline at end of file + url: ${CCD_DATA_STORE_URL:localhost:4452} diff --git a/src/main/resources/db/migration/V075__fee_payment.sql b/src/main/resources/db/migration/V075__fee_payment.sql new file mode 100644 index 0000000000..1e7cb8b0f7 --- /dev/null +++ b/src/main/resources/db/migration/V075__fee_payment.sql @@ -0,0 +1,14 @@ +CREATE TABLE fee_payment ( + id UUID NOT NULL, + claim_id UUID NOT NULL, + party_id UUID NOT NULL, + request_date TIMESTAMP NOT NULL, + request_reference VARCHAR(255), + external_reference VARCHAR(255), + amount NUMERIC(19, 2), + payment_status VARCHAR(50), + + CONSTRAINT pk_fee_payment PRIMARY KEY (id), + CONSTRAINT fk_fee_payment_claim FOREIGN KEY (claim_id) REFERENCES claim (id), + CONSTRAINT fk_fee_payment_party FOREIGN KEY (party_id) REFERENCES party (id) +); diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentServiceTest.java index 5db46b76db..5ca4f44ccd 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/feesandpay/service/PaymentServiceTest.java @@ -13,14 +13,29 @@ import uk.gov.hmcts.reform.payments.client.models.FeeDto; import uk.gov.hmcts.reform.payments.request.CreateServiceRequestDTO; import uk.gov.hmcts.reform.payments.response.PaymentServiceResponse; +import uk.gov.hmcts.reform.pcs.ccd.entity.ClaimEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.feesandpay.FeePaymentEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; +import uk.gov.hmcts.reform.pcs.ccd.repository.feeandpay.FeePaymentRepository; +import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; +import uk.gov.hmcts.reform.pcs.exception.CaseNotFoundException; import uk.gov.hmcts.reform.pcs.feesandpay.mapper.PaymentRequestMapper; import uk.gov.hmcts.reform.pcs.feesandpay.model.FeeDetails; +import uk.gov.hmcts.reform.pcs.feesandpay.model.PaymentStatus; +import uk.gov.hmcts.reform.pcs.feesandpay.model.ServiceRequestUpdate; import uk.gov.hmcts.reform.pcs.idam.IdamService; import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -29,84 +44,176 @@ @ExtendWith(MockitoExtension.class) class PaymentServiceTest { + private static final long CASE_REFERENCE = 123L; + private static final String CCD_CASE_NUMBER = "1111-2222-3333-4444"; + private static final int VOLUME = 2; + private static final String RESPONSIBLE_PARTY = "Applicant"; + private static final String SYSTEM_TOKEN = "Bearer sys-token"; + private static final BigDecimal CALCULATED_AMOUNT = new BigDecimal("808.00"); + private static final String FEE_CODE = "FEE0412"; + private static final String FEE_VERSION = "4"; + private static final String SERVICE_REQUEST_REFERENCE = "SR-123"; + private static final String CALLBACK_URL = "https://etc:123/service-request-update"; + private static final String HMCTS_ORG_ID = "TEST_ORG"; + @Mock private PaymentsClient paymentsClient; - @Mock private PaymentRequestMapper paymentRequestMapper; - @Mock private IdamService idamService; + @Mock + private FeePaymentRepository feePaymentRepository; + @Mock + private PcsCaseService pcsCaseService; @InjectMocks - private PaymentService paymentService; + private PaymentService underTest; @Captor private ArgumentCaptor createServiceRequestCaptor; @BeforeEach - void setUp() throws Exception { - var callbackUrlField = PaymentService.class.getDeclaredField("callbackUrl"); - callbackUrlField.setAccessible(true); - callbackUrlField.set(paymentService, "https://callback"); - - var hmctsOrgIdField = PaymentService.class.getDeclaredField("hmctsOrgId"); - hmctsOrgIdField.setAccessible(true); - hmctsOrgIdField.set(paymentService, "TEST_ORG"); + void setUp() { + setPrivateField(underTest, "callbackUrl", CALLBACK_URL); + setPrivateField(underTest, "hmctsOrgId", HMCTS_ORG_ID); } @Test void shouldCreateServiceRequestSuccessfully() { - String caseReference = "BUS-123"; - String ccdCaseNumber = "1111-2222-3333-4444"; - int volume = 2; - String responsibleParty = "Applicant"; - String systemToken = "Bearer sys-token"; - - FeeDto mappedFee = FeeDto.builder() - .calculatedAmount(new BigDecimal("808.00")) - .code("FEE0412") - .version("4") - .volume(volume) - .build(); + // Given + FeeDetails feeDetails = mock(FeeDetails.class); + paymentsClientDependencies(feeDetails); + ClaimPartyEntity claimPartyEntity = claimPartyEntity(); + PcsCaseEntity pcsCaseEntity = setupPcsCase(claimPartyEntity); + when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(pcsCaseEntity); + PaymentServiceResponse expectedResponse = createPaymentServiceResponse(); + when(paymentsClient.createServiceRequest(any(), any(CreateServiceRequestDTO.class))) + .thenReturn(expectedResponse); + + // When + PaymentServiceResponse result = underTest.createServiceRequest(String.valueOf(CASE_REFERENCE), CCD_CASE_NUMBER, + feeDetails, VOLUME, RESPONSIBLE_PARTY); + + // Then + assertServiceRequestCreation(result); + } - CasePaymentRequestDto casePaymentRequestDto = CasePaymentRequestDto.builder() - .action("payment") - .responsibleParty(responsibleParty) - .build(); + @Test + void shouldCreateServiceRequest_NoPCSCase() { + // Given + FeeDetails feeDetails = mock(FeeDetails.class); + when(pcsCaseService.loadCase(anyLong())).thenThrow(new CaseNotFoundException(222L)); - PaymentServiceResponse paymentResponse = PaymentServiceResponse.builder() - .serviceRequestReference("SR-123") - .build(); + // When + assertThatThrownBy(() -> underTest.createServiceRequest("222", CCD_CASE_NUMBER, + feeDetails, VOLUME, RESPONSIBLE_PARTY)) + .isInstanceOf(CaseNotFoundException.class); + } + @Test + void shouldCreateServiceRequest_NoClaimPartyEntityFound() { + // Given FeeDetails feeDetails = mock(FeeDetails.class); - when(paymentRequestMapper.toFeeDto(feeDetails, volume)).thenReturn(mappedFee); - when(paymentRequestMapper.toCasePaymentRequest(responsibleParty)) + ClaimPartyEntity claimPartyEntity = mock(ClaimPartyEntity.class); + PartyEntity partyEntity = mock(PartyEntity.class); + when(partyEntity.getOrgName()).thenReturn("different"); + when(claimPartyEntity.getParty()).thenReturn(partyEntity); + PcsCaseEntity pcsCaseEntity = setupPcsCase(claimPartyEntity); + when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(pcsCaseEntity); + + // When ... Then + assertThatThrownBy(() -> underTest.createServiceRequest(String.valueOf(CASE_REFERENCE), CCD_CASE_NUMBER, + feeDetails, VOLUME, RESPONSIBLE_PARTY)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Matching PartyEntity not found"); + + } + + @Test + void shouldProcessPaymentResponse() { + // Given + String requestReference = UUID.randomUUID().toString(); + ServiceRequestUpdate serviceRequestUpdate = ServiceRequestUpdate.builder() + .serviceRequestReference(requestReference).serviceRequestStatus(PaymentStatus.PAID.getValue()) + .build(); + FeePaymentEntity feePaymentEntity = FeePaymentEntity.builder().build(); + when(feePaymentRepository.findByRequestReference(requestReference)).thenReturn(Optional.of(feePaymentEntity)); + + // When + underTest.processPaymentResponse(serviceRequestUpdate); + + // Then + verify(feePaymentRepository).findByRequestReference(requestReference); + verify(feePaymentRepository).save(any(FeePaymentEntity.class)); + } + + private void paymentsClientDependencies(FeeDetails feeDetails) { + FeeDto mappedFee = createFeeDto(); + CasePaymentRequestDto casePaymentRequestDto = createCasePaymentRequestDto(); + when(paymentRequestMapper.toFeeDto(feeDetails, VOLUME)).thenReturn(mappedFee); + when(paymentRequestMapper.toCasePaymentRequest(RESPONSIBLE_PARTY)) .thenReturn(casePaymentRequestDto); - when(idamService.getSystemUserAuthorisation()).thenReturn(systemToken); - when(paymentsClient.createServiceRequest(eq(systemToken), any(CreateServiceRequestDTO.class))) - .thenReturn(paymentResponse); - - PaymentServiceResponse result = paymentService.createServiceRequest( - caseReference, - ccdCaseNumber, - feeDetails, - volume, - responsibleParty - ); + when(idamService.getSystemUserAuthorisation()).thenReturn(SYSTEM_TOKEN); + } + + private PcsCaseEntity setupPcsCase(ClaimPartyEntity claimPartyEntity) { + ClaimEntity claimEntity = mock(ClaimEntity.class); + when(claimEntity.getClaimParties()).thenReturn(List.of(claimPartyEntity)); + PcsCaseEntity pcsCaseEntity = mock(PcsCaseEntity.class); + when(pcsCaseEntity.getClaims()).thenReturn(List.of(claimEntity)); + return pcsCaseEntity; + } + + private ClaimPartyEntity claimPartyEntity() { + ClaimPartyEntity claimPartyEntity = mock(ClaimPartyEntity.class); + PartyEntity partyEntity = mock(PartyEntity.class); + when(partyEntity.getOrgName()).thenReturn(RESPONSIBLE_PARTY); + when(claimPartyEntity.getParty()).thenReturn(partyEntity); + return claimPartyEntity; + } + + private CasePaymentRequestDto createCasePaymentRequestDto() { + return CasePaymentRequestDto.builder().action("payment").responsibleParty(RESPONSIBLE_PARTY).build(); + } + private PaymentServiceResponse createPaymentServiceResponse() { + return PaymentServiceResponse.builder().serviceRequestReference(SERVICE_REQUEST_REFERENCE).build(); + } + + private FeeDto createFeeDto() { + return FeeDto.builder().calculatedAmount(CALCULATED_AMOUNT).code(FEE_CODE).version(FEE_VERSION) + .volume(VOLUME).build(); + } + + private void assertServiceRequestCreation(PaymentServiceResponse result) { assertThat(result).isNotNull(); - assertThat(result.getServiceRequestReference()).isEqualTo("SR-123"); + assertThat(result.getServiceRequestReference()).isEqualTo(SERVICE_REQUEST_REFERENCE); - verify(paymentsClient).createServiceRequest(eq(systemToken), createServiceRequestCaptor.capture()); + verify(paymentsClient).createServiceRequest(eq(SYSTEM_TOKEN), createServiceRequestCaptor.capture()); CreateServiceRequestDTO sent = createServiceRequestCaptor.getValue(); - assertThat(sent.getCallBackUrl()).isEqualTo("https://callback"); - assertThat(sent.getHmctsOrgId()).isEqualTo("TEST_ORG"); - assertThat(sent.getCaseReference()).isEqualTo(caseReference); - assertThat(sent.getCcdCaseNumber()).isEqualTo(ccdCaseNumber); + + assertCreateServiceRequestDTO(sent); + } + + private void assertCreateServiceRequestDTO(CreateServiceRequestDTO sent) { + assertThat(sent.getCallBackUrl()).isEqualTo(CALLBACK_URL); + assertThat(sent.getHmctsOrgId()).isEqualTo(HMCTS_ORG_ID); + assertThat(sent.getCaseReference()).isEqualTo(String.valueOf(CASE_REFERENCE)); + assertThat(sent.getCcdCaseNumber()).isEqualTo(CCD_CASE_NUMBER); assertThat(sent.getFees()).isNotNull(); assertThat(sent.getFees()).hasSize(1); - assertThat(sent.getFees()[0]).isEqualTo(mappedFee); - assertThat(sent.getCasePaymentRequest()).isEqualTo(casePaymentRequestDto); + assertThat(sent.getFees()[0]).isEqualTo(createFeeDto()); } + + private static void setPrivateField(T object, String fieldName, Object value) { + try { + var field = PaymentService.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(object, value); + } catch (Exception e) { + throw new RuntimeException("Failed to set private field", e); + } + } + } diff --git a/src/test/resources/wiremock/mappings/payment-success.json b/src/test/resources/wiremock/mappings/payment-success.json new file mode 100644 index 0000000000..acf8632086 --- /dev/null +++ b/src/test/resources/wiremock/mappings/payment-success.json @@ -0,0 +1,34 @@ +{ + "request": { + "method": "POST", + "urlPath": "/payments/service-request", + "bodyPatterns": [ + { "matchesJsonPath": "$.ccd_case_number" } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "service_request_reference": "SR-{{{jsonPath request.body '$.ccd_case_number'}}}" + }, + "transformers": ["response-template"] + }, + "serveEventListeners": [ + { + "name": "webhook", + "parameters": { + "method": "POST", + "url": "{{{jsonPath originalRequest.body '$.call_back_url'}}}", + "headers": { + "Content-Type": "application/json", + "Authorization": "{{{originalRequest.headers.Authorization}}}", + "ServiceAuthorization": "{{{originalRequest.headers.ServiceAuthorization}}}" + }, + "body": "{\"service_request_reference\": \"SR-{{{jsonPath originalRequest.body '$.ccd_case_number'}}}\", \"ccd_case_number\": \"{{{jsonPath originalRequest.body '$.ccd_case_number'}}}\", \"service_request_amount\": 2500, \"service_request_status\": \"Paid\", \"payment\": { \"payment_amount\": 2500, \"payment_reference\": \"RC-{{date format='yyyy'}}-{{randomValue length=4 type='NUMERIC'}}\", \"payment_method\": \"payment by account\", \"case_reference\": \"{{{jsonPath originalRequest.body '$.case_reference'}}}\", \"account_number\": \"PBA{{randomValue length=7 type='NUMERIC'}}\" }}" + } + } + ] +} diff --git a/wiremock-local.yml b/wiremock-local.yml new file mode 100644 index 0000000000..8618624489 --- /dev/null +++ b/wiremock-local.yml @@ -0,0 +1,9 @@ +services: + wiremock: + image: wiremock/wiremock:latest + container_name: wiremock-local + ports: + - "8083:8083" + volumes: + - ./src/test/resources/wiremock/mappings:/home/wiremock/mappings:ro + command: --global-response-templating --verbose --port 8083