class CancelPaymentService {

    public ResponseObject<CancelPaymentResponse> initiatePaymentCancellation(SpiPayment payment, String encryptedPaymentId,
                                                                             Boolean tppExplicitAuthorisationPreferred,
                                                                             TppRedirectUri tppRedirectUri) {
        SpiContextData spiContextData = spiContextDataProvider.provide();

        SpiAspspConsentDataProvider aspspConsentDataProvider =
            aspspConsentDataProviderFactory.getSpiAspspDataProviderFor(encryptedPaymentId);

        SpiResponse<SpiPaymentCancellationResponse> spiResponse = paymentCancellationSpi.initiatePaymentCancellation(spiContextData, payment, aspspConsentDataProvider);

        if (spiResponse.hasError()) {
            ErrorHolder errorHolder = spiErrorMapper.mapToErrorHolder(spiResponse, ServiceType.PIS);
            log.info("Payment-ID [{}]. Initiate Payment Cancellation has failed. Error msg: {}.",
                     encryptedPaymentId, errorHolder);
            return ResponseObject.<CancelPaymentResponse>builder()
                       .fail(errorHolder)
                       .build();
        }

        CancelPaymentResponse cancelPaymentResponse = spiToXs2aCancelPaymentMapper.mapToCancelPaymentResponse(spiResponse.getPayload(), payment, encryptedPaymentId);
        TransactionStatus resultStatus = cancelPaymentResponse.getTransactionStatus();

        if (resultStatus != null) {
            updatePaymentStatusAfterSpiService.updatePaymentStatus(encryptedPaymentId, resultStatus);
        } else {
            resultStatus = payment.getPaymentStatus();
            cancelPaymentResponse.setTransactionStatus(resultStatus);
        }

        UUID internalRequestId = requestProviderService.getInternalRequestId();

        updatePaymentStatusAfterSpiService.updatePaymentCancellationTppRedirectUri(encryptedPaymentId, tppRedirectUri);
        updatePaymentStatusAfterSpiService.updatePaymentCancellationInternalRequestId(encryptedPaymentId, internalRequestId.toString());
        cancelPaymentResponse.setInternalRequestId(internalRequestId.toString());

        if (resultStatus == TransactionStatus.CANC) {
            log.info("Payment-ID [{}]. Initiate Payment Cancellation has failed. Payment status - CANCELED", encryptedPaymentId);
            return ResponseObject.<CancelPaymentResponse>builder()
                       .body(cancelPaymentResponse)
                       .build();
        }

        if (resultStatus.isFinalisedStatus()) {
            log.info("Payment-ID [{}]. Initiate Payment Cancellation has failed. Payment has finalised status", encryptedPaymentId);
            return ResponseObject.<CancelPaymentResponse>builder()
                       .fail(ErrorType.PIS_400, of(MessageErrorCode.RESOURCE_BLOCKED))
                       .build();
        }

        if (resultStatus == TransactionStatus.RCVD
                || cancellationScaNeededDecider.isNoScaRequired(cancelPaymentResponse.isStartAuthorisationRequired())) {
            payment.setPaymentStatus(resultStatus);
            return proceedNoScaCancellation(payment, spiContextData, aspspConsentDataProvider, encryptedPaymentId);
        }

        // in payment cancellation case 'multilevelScaRequired' is always false
        boolean implicitMethod = authorisationMethodDecider.isImplicitMethod(tppExplicitAuthorisationPreferred, false);
        if (implicitMethod) {
            Xs2aCreatePisAuthorisationRequest request = new Xs2aCreatePisAuthorisationRequest(encryptedPaymentId, new PsuIdData(null, null, null, null, null), payment.getPaymentProduct(), payment.getPaymentType(), null);
            ResponseObject<CancellationAuthorisationResponse> authorisationResponse = paymentCancellationAuthorisationService.createPisCancellationAuthorisation(request);

            if (authorisationResponse.hasError()) {
                log.info("Payment-ID [{}]. Initiate Payment Cancellation has failed. Can't create implicit authorisation",
                         encryptedPaymentId);
                return ResponseObject.<CancelPaymentResponse>builder()
                           .fail(ErrorType.PIS_CANC_405, of(MessageErrorCode.CANCELLATION_INVALID))
                           .build();
            }

            CancellationAuthorisationResponse authorisationResponseBody = authorisationResponse.getBody();
            cancelPaymentResponse.setAuthorizationId(authorisationResponseBody.getAuthorisationId());
            cancelPaymentResponse.setScaStatus(authorisationResponseBody.getScaStatus());
        }

        return ResponseObject.<CancelPaymentResponse>builder()
                   .body(cancelPaymentResponse)
                   .build();

    }

}