Construyendo un CSM Service implementación

Esta guía explicará cómo iniciar su propio CSM service implementación utilizando la biblioteca inicial predefinida del servicio CSM.

Propósito

Todo IPF CSM services implemente los diversos pagos de IPF APIs(como se documenta en APIs), incluyendo:

  • Aclarar y Liquidar

  • Recibir Pago

  • Enviar Recordatorio a CSM

Y así sucesivamente.

El CSM service la biblioteca de inicio agrupa los siguientes componentes para facilitar el desarrollo para aquellos que desean crear su propio CSM service implementación:

  • Definiciones de API

  • Kafka y JMS enlaces para la comunicación entre un IPF application y el CSM service

  • Interfaces predeterminadas para recibir mensajes de la IPF application, que luego se envían al esquema

  • Implementaciones predeterminadas para el reenvío de mensajes desde el esquema al IPF application

Pasos

Aquí está cómo comenzar a crear su propio CSM service implementación.

Paso 1: Agregue las dependencias requeridas

Las dependencias que debe agregar dependen de qué enlaces de transporte desea utilizar en su implementación.

Estas dependencias están todas disponibles como parte de la ipf-bom. Si usted está utilizando esto, no necesita especificar ninguna versión ya que recibirá el CSM Service Versión de la biblioteca inicial que ha sido validada para el ipf-bom en uso.
Tipo de transporte Dependencia

Kafka

<dependency>
    <groupId>com.iconsolutions.ipf.payments.csm</groupId>
    <artifactId>csm-service-starter-kafka</artifactId>
</dependency>

JMS

<dependency>
    <groupId>com.iconsolutions.ipf.payments.csm</groupId>
    <artifactId>csm-service-starter-jms</artifactId>
</dependency>

Paso 2: Implementar API interfaces para recibir mensajes de IPF para reenviar al esquema

`csm-service-starter- * `proporciona dos API interfaces para recibir mensajes de un IPF application que luego será enviado al esquema:

  • com.iconsolutions.instantpayments.csm.ct. CsmApiReceiver: para mensajes de tipo transferencia de crédito

  • com.iconsolutions.instantpayments.csm.rrr. CsmRApiReceiver: para la recuperación/devolución/resultado de mensajes de tipo investigación

  • com.iconsolutions.instantpayments.csm.dd. CsmDDApiReceiver: para mensajes de tipo domiciliación bancaria

Las implementaciones de estas interfaces deben ser Spring bean clases, y no hay implementaciones de métodos predeterminadas.

Tomando el CsmApiReceiver la interfaz como ejemplo, la implementación más simple se verá algo así:

import com.iconsolutions.instantpayments.csm.ct. CsmApiReceiver;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import org.springframework.stereotype. Component;
import java.util.concurrent. CompletableFuture;
import java.util.concurrent. CompletionStage;
// Additional required imports here..

@Component (1)
public class MyCsmApiReceiver implements CsmApiReceiver { (2)

    @Override
    public CompletionStage<Void> clearAndSettle(ClearAndSettleRequest clearAndSettleRequest) {
        return CompletableFuture.completedStage(null);
    }

    // Other overridden methods here..

}
1 Definiendo la clase de implementación como un Spring bean
2 Implementando el receptor de (transferencia de crédito)API interfaz === Paso 3: Maneje una solicitud entrante de IPF y reenvíe al esquema La implementación de cada método en esta clase depende de cómo desea procesar el mensaje entrante (dentro de los límites de la flujo de trabajo soportado para esto message type). Usando el clearAndSettle método como un ejemplo (que define el IPF application to CSM Service interfaz para el Flujo de Transferencia de Crédito del Deudor), el procesamiento del mensaje, en su forma más simple, típicamente implicaría:
  1. reenviando la solicitud entrante de IPF a un destino desde el cual un esquema puede consumirla

  2. enviar un acuse de recibo (respuesta técnica) a IPF después de que la solicitud haya sido entregada a su destino

csm-service-starter- * `proporciona el send connector para enviar el acuse de recibo a IPF (`TechnicalResponseSender) y el tipo de respuesta de reconocimiento requerida (TechnicalResponse), pero necesitaremos crear el nuestro.send connector enviar la solicitud entrante a la CSM.

Las ubicaciones predeterminadas desde las cuales se consumen los mensajes por los ReceiveConnectors proporcionados y se producen por los SendConnectors proporcionados se pueden encontrar en el referencia de transporte página. Puede utilizar las rutas de configuración en esta página (listadas bajo el Config Key columnas en la tabla) para anular estos valores de ubicación predeterminados según sea necesario.

Este SendConnector se deberá proporcionar con:

  • El transporte apropiado para el CSM(Kafka or JMS)

  • Una función para transformar la solicitud entrante (ClearAndSettleRequest) al objetivo CSM tipo de esquema (específico del esquema pacs. 008) y enriquecer valores estáticos específicos en el mensaje transformado, por ejemplo, agente que instruye/instruido, sistema de compensación, instrumento local, etc.

  • Una función para convertir el esquema específico transformado pacs. 008 mensaje al formato de salida objetivo, generalmente XML o de propiedad del banco XML

Transporte

Por favor, consulte el Kafka Introducción Rápida or JMS Quickstart páginas para orientación sobre la creación de su propio SendConnector transporte.

Transforme la solicitud entrante al objetivo CSM tipo de esquema

La forma más sencilla de transformar la solicitud entrante al objetivo CSM tipo de esquema y enriquecer el mensaje transformado es utilizar el IPF mapping framework y aproveche el proporcionado com.iconsolutions.instantpayments.csm. MapperRegistry. Primero, cree un Spring bean clase que implementa el com.iconsolutions.instantpayments.csm. MessageMapper<FromType, ToType> interfaz y contiene su mapping función:

import com.iconsolutions.instantpayments.csm. MessageMapper;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.model. Header;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import com.iconsolutions.ipf.transformation. TransformationService;
import lombok. RequiredArgsConstructor;
import lombok.extern.slf4j. Slf4j;
import org.springframework.stereotype. Component;
import mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. FIToFICustomerCreditTransferV08;

@Slf4j
@RequiredArgsConstructor
@Component
public class IpfToMyCsmPacs008Mapper implements MessageMapper<ClearAndSettleRequest, MyCsmOutboundCTRequest> { (1)

    private final TransformationService transformationService; (2)

    @Override
    public Class<ClearAndSettleRequest> supportedType() { (3)
        return ClearAndSettleRequest.class;
    }

    @Override
    public MyCsmOutboundCTRequest map(ClearAndSettleRequest request) {
        log.debug("Mapping ClearAndSettleRequest with id: {}", request.getRequestId());
        var fi2fi = transformationService.mapThenEnrichWithDefault(request.getPayload().getContent(), FIToFICustomerCreditTransferV08.class); (4)
        // Set some additional fields (e.g. accptncDtTm) on the transformed message here
        return new MyCsmOutboundCTRequest(new Header(request.getRequestId()), fi2fi); (5)
    }
}
1 Implementando el MessageMapper interfaz, con FromType=ClearAndSettleRequest y ToType=MyCsmOutboundCTRequest
2 Cableado en el IPF mapping framework TransformationService
3 Instruyendo al MappingRegistry para usar esto mapper cuando mapping a ClearAndSettleRequest tipo
4 Transformando la carga útil de la solicitud de un IPF ISO20022 canónico pacs. 008 a su equivalente específico del esquema
5 Creando un nuevo MyCsmOutboundCTRequest, que es un custom objeto que le permite enviar un Header con su mensaje en el SendConnector:
import mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. FIToFICustomerCreditTransferV08;
import com.myorg.myproject.mycsm.model. Header;
import lombok. AllArgsConstructor;
import lombok. Data;

@Data
@AllArgsConstructor
public class MyCsmOutboundCTRequest {
    private Header header;
    private FIToFICustomerCreditTransferV08 payload;
}
import lombok. AllArgsConstructor;
import lombok. Data;

@Data
@AllArgsConstructor
public class Header {
    private String aUsefulHeaderId;
}

Ahora conecte esto mapper, a través de la MapperRegistry, en un Spring bean clase que actuará como base para todos sus mapping lógica:

import com.iconsolutions.instantpayments.csm. MapperRegistry;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import org.springframework.stereotype. Component;

@Component
public class MyCsmMapper {

    private final MapperRegistry mapperRegistry; (1)

    public MyCsmMapper(final MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
    }

    public MyCsmOutboundCTRequest map(ClearAndSettleRequest clearAndSettleRequest) {
        return mapperRegistry.map(clearAndSettleRequest);
    }
}
1 Cableado en el MapperRegistry que ahora contiene el IpfToMyCsmPacs008Mapper
Convertido transformado pacs. 008 mensaje al formato de salida objetivo

El formato de mensaje de salida objetivo para un CSM típicamente será XML. Por lo tanto, necesitamos convertir el esquema específico pacs. 008 java objeto en XML antes de enviar.

Primero, necesitaremos crear un JAXBContext conteniendo el esquema específico pacs. 008 Definición del documento:

private JAXBContext myCsmJAXBContext() throws JAXBException {
    return JAXBContext.newInstance(mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. Document.class);
}

Entonces necesitaremos crear un marshaller utilizando el IPF proporcionado.com.iconsolutions.mapper. JaxbObjectToStringMapper<> y el JAXBContext creado arriba:

private JaxbObjectToStringMapper<Object> marshaller() {
    return new JaxbObjectToStringMapper<>(myCsmJAXBContext(), false); (1)
}
1 Estableciendo "jaxb.formatted.output" a false ya que no necesitamos JAXB para formatear la salida aquí

Si lo desea, tiene la opción de incluir la validación de esquema en el marshaller. Para ello, primero necesitamos crear un Resource del esquema pacs. 008 xsd file:

@Value("classpath:xsd/mycsm_pacs_008.xsd")
private Resource myCsmPacs008Xsd;

Luego creamos nuestro Schema definición de la siguiente manera:

private Schema myCsmSchema() throws IOException, SAXException {
    StreamSource[] schemaStreamSources = {
            new StreamSource(myCsmPacs008Xsd.getInputStream())
    };
    return SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(schemaStreamSources);
}

Y podemos entonces incluir esto dentro de nuestra definición de marshaller:

private JaxbObjectToStringMapper<Object> marshaller() {
    return new JaxbObjectToStringMapper<>(myCsmJAXBContext(), myCsmSchema(), false);
}

Luego utilizamos nuestro marshaller para convertir el MyCsmOutboundCTRequest a nuestro formato de mensaje objetivo (transporte):

public TransportMessage convertToTransport(MyCsmOutboundCTRequest request) {
    var myCsmPacs008ObjectFactory = new mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. ObjectFactory();
    var document = myCsmPacs008ObjectFactory.createDocument();
    document.setFIToFICstmrCdtTrf(request.getPayload());
    var convertedMessage = marshaller.map(document);
    log.debug("Converted MyCsmOutboundCTRequest to transport message type: {}", convertedMessage);
    return new TransportMessage(convertedMessage);
}

Incluyendo todo lo anterior,MyCsmMapper ahora se ve así:

import lombok.extern.slf4j. Slf4j;
import org.springframework.stereotype. Component;
import com.iconsolutions.instantpayments.csm. MapperRegistry;
import com.iconsolutions.mapper. JaxbObjectToStringMapper;
import org.springframework.core.io. Resource;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import com.iconsolutions.ipf.core.connector.message. TransportMessage;
import jakarta.xml.bind. JAXBContext;
import javax.xml.transform.stream. StreamSource;
import javax.xml.validation. SchemaFactory;
import jakarta.xml.bind. JAXBException;
import org.xml.sax. SAXException;
import java.io. IOException;

@Slf4j
@Component
public class MyCsmMapper {

    private final MapperRegistry mapperRegistry;
    private final JaxbObjectToStringMapper<Object> marshaller;

    public MyCsmMapper(@Value("classpath:xsd/mycsm_pacs_008.xsd") Resource myCsmPacs008Xsd,
                       final MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
        this.marshaller = marshaller();
    }

    public MyCsmOutboundCTRequest map(ClearAndSettleRequest clearAndSettleRequest) {
        return mapperRegistry.map(clearAndSettleRequest);
    }

    public TransportMessage convertToTransport(MyCsmOutboundCTRequest request) {
        var myCsmpacs008ObjectFactory = new mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. ObjectFactory();
        var document = myCsmpacs008ObjectFactory.createDocument();
        document.setFIToFICstmrCdtTrf(request.getPayload());
        var convertedMessage = marshaller.map(document);
        log.debug("Converted MyCsmOutboundCTRequest to transport message type: {}", convertedMessage);
        return new TransportMessage(convertedMessage);
    }

    private JaxbObjectToStringMapper<Object> marshaller() {
        return new JaxbObjectToStringMapper<>(myCsmJAXBContext(), myCsmSchema(), false);
    }

    private JAXBContext myCsmJAXBContext() throws JAXBException {
        return JAXBContext.newInstance(mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. Document.class);
    }

    private Schema myCsmSchema() throws IOException, SAXException {
        StreamSource[] schemaStreamSources = {
                new StreamSource(myCsmPacs008Xsd.getInputStream())
        };
        return SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(schemaStreamSources);
    }
}

Ahora tenemos todos los componentes requeridos para el SendConnector, podemos proceder a crearlo:

import akka.actor. ClassicActorSystemProvider;
import com.iconsolutions.ipf.core.connector. SendConnector;
import com.iconsolutions.ipf.core.connector.transport. ConnectorTransport;
import com.iconsolutions.ipf.core.messagelogger. MessageLogger;
import com.iconsolutions.ipf.core.shared.correlation. CorrelationId;
import com.iconsolutions.ipf.core.shared.correlation. CorrelationService;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.mapper.ct. MyCsmMapper;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import org.springframework.boot.autoconfigure. AutoConfiguration;
import org.springframework.context.annotation. Bean;

@AutoConfiguration
public class MyCsmConfig {

    private final ClassicActorSystemProvider actorSystem;
    private final CorrelationService correlationService;
    private final MessageLogger messageLogger;

    public MyCsmConfig(ClassicActorSystemProvider actorSystem,
                       CorrelationService correlationService,
                       MessageLogger messageLogger) {
        this.actorSystem = actorSystem;
        this.correlationService = correlationService;
        this.messageLogger = messageLogger;
    }

    @Bean
    SendConnector<ClearAndSettleRequest, MyCsmOutboundCTRequest> clearAndSettleRequestSendConnector(MyCsmMapper mapper,
                                                                                                    ConnectorTransport<MyCsmOutboundCTRequest> transport) {
        String connectorName = "myCsmClearAndSettleRequestSender";
        String configRoot = "my-csm.send-cas-connector";
        return SendConnector.<ClearAndSettleRequest, MyCsmOutboundCTRequest>builder(connectorName, configRoot, actorSystem)
                .withMessageLogger(messageLogger)
                .withCorrelationService(correlationService)
                .withConnectorTransport(transport)
                .withDomainToTargetTypeConverter(clearAndSettleRequest -> mapper.map(clearAndSettleRequest)) (1)
                .withSendTransportMessageConverter(outboundRequest -> mapper.convertToTransport(outboundRequest)) (2)
                .withCorrelationIdExtractor(outboundRequest -> CorrelationId.of(outboundRequest.getPayload().getGrpHdr().getMsgId()))
                .build();
    }
}
1 Usando nuestro `MyCsmMapper 's ` map() method to transform the incoming ` ClearAndSettleRequest ` to the output ` Tipo MyCsmOutboundCTRequest
2 Usando nuestro `MyCsmMapper 's ` convertToTransport() method to convert the ` MyCsmOutboundCTRequest` a la salida objetivo XML formato
Por favor, visite el Sending Connector página si no está familiarizado con ninguno de los otros SendConnector opciones proporcionadas arriba.

Con nuestra SendConnector ahora creado, podemos proporcionar una implementación para el clearAndSettle método en nuestro MyCsmApiReceiver clase:

import com.iconsolutions.instantpayments.csm.ct. CsmApiReceiver;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import com.iconsolutions.instantpayments.csm. TechnicalResponseSender;
import lombok. RequiredArgsConstructor;
import org.springframework.stereotype. Component;
import java.util.concurrent. CompletionStage;
import java.time. Instant;
import java.util. UUID;
// Additional required imports here..

@RequiredArgsConstructor
@Component
public class MyCsmApiReceiver implements CsmApiReceiver { (2)

    private final SendConnector<ClearAndSettleRequest, MyCsmOutboundCTRequest> clearAndSettleRequestSendConnector;
    private final TechnicalResponseSender technicalResponseSender;

    @Override
    public CompletionStage<Void> clearAndSettle(final ClearAndSettleRequest request) {
        log.debug("Handling clear and settle request for id: {}", request.getRequestId());
        return clearAndSettleRequestSendConnector.send(request.getProcessingContext(), request) (1)
                .thenCompose(deliveryOutcome -> {
                    final var sendOutcomeSuccess = deliveryOutcome.getDeliveryReport().getOutcome() == DeliveryReport. Outcome. SUCCESS;
                    return technicalResponseSender.send(TechnicalResponse.builder() (2)
                            .responseId(UUID.randomUUID().toString())
                            .requestId(request.getRequestId())
                            .processingContext(request.getProcessingContext())
                            .version(request.getVersion())
                            .createdAt(Instant.now())
                            .ipfId(request.getIpfId())
                            .responseCode(StringUtils. EMPTY)
                            .status(sendOutcomeSuccess? PaymentResponse. Status. SUCCESS: PaymentResponse. Status. FAILURE)
                            .reason(sendOutcomeSuccess? "": deliveryOutcome.getDeliveryReport().getDeliveryException().toString())
                            .build());
                });
    }

    // Other overridden methods here..

}
1 Enviando el mensaje de solicitud de aclaración y liquidación recibido por el CSM service a un destino configurado utilizando nuestro ClearAndSettleSendConnector
2 Enviando una respuesta técnica de vuelta al IPF application después de que se haya entregado el mensaje de solicitud clara y asentada utilizando el SendConnector y el tipo de respuesta proporcionado en el `csm-service-starter- * ` biblioteca

Paso 4: Maneje una respuesta entrante de un CSM

En el paso anterior, implementamos un método para manejar una solicitud de liquidación y aclaración entrante de IPF. Ahora necesitamos proporcionar una forma de manejar la respuesta que recibimos para esta solicitud de la CSM. Para este escenario,`csm-service-starter- * ` proporciona un SendConnector para reenviar la respuesta de vuelta a IPF, pero necesitaremos crear nuestro propio ReceiveConnector para consumir la respuesta de la CSM.

Este ReceiveConnector deberá ser provisto con:

  • El transporte apropiado para el CSM(Kafka or JMS)

  • Una función que convierte el XML informe de estado de pago formateado del esquema al esquema específico pacs. 002 java objeto, y luego transforma esto en un ClearAndSettleResponse

  • Una función para reenviar el ClearAndSettleResponse a IPF

Transporte

Por favor, consulte el Kafka Introducción Rápida or JMS Quickstart páginas para orientación sobre la creación de su propio Recibir Connector transport.

Convierta el informe de estado de pago del esquema en ClearAndSettleResponse.

Así como lo hicimos anteriormente en Paso 3, la etapa inicial en la conversión del informe de estado de pago XML de la esquema al esquema específico pacs. 002 java El objetivo es crear un JAXBContext que contenga el esquema específico.pacs. 002 Definición del documento. Puede añadir esta definición a nuestro JAXBContext previamente creado:

private JAXBContext myCsmJAXBContext() throws JAXBException {
    return JAXBContext.newInstance(
            mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. Document.class,
            mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_002_001_10. Document.class
        );
}

Entonces, necesitaremos crear un unmarshalling utilizando el IPF proporcionado.com.iconsolutions.mapper. StringToJaxbObjectMapper<> y el JAXBContext creado arriba:

private StringToJaxbObjectMapper<Object> unmarshaller() {
    return new StringToJaxbObjectMapper<>(myCsmJAXBContext());
}

Nuevamente, usted tiene la opción de incluir la validación de esquema en el deserializador. Para hacer esto, necesitamos crear otro Resource del esquema pacs. 002 xsd file:

@Value("classpath:xsd/mycsm_pacs_002.xsd")
private Resource myCsmPacs002Xsd;

Luego añadimos esto a nuestro previamente creado Schema definición:

private Schema myCsmSchema() throws IOException, SAXException {
    StreamSource[] schemaStreamSources = {
            new StreamSource(myCsmPacs008Xsd.getInputStream()),
            new StreamSource(myCsmPacs002Xsd.getInputStream())
    };
    return SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(schemaStreamSources);
}

Y podemos entonces incluir esto dentro de nuestra definición de unmarshalling:

private StringToJaxbObjectMapper<Object> unmarshaller() {
    return new StringToJaxbObjectMapper<>(myCsmJAXBContext(), myCsmSchema());
}

Luego utilizamos nuestro unmarshaler para convertir el informe de estado de pago. XML del esquema al esquema específico pacs. 002 java objeto:

var messageXml = transportMessage.getPayload().toString();
var myCsmPacs002 = unmarshaller.map(messageXml);

Para transformar el esquema específico deserializado pacs. 002 a un ClearAndSettleResponse, podemos nuevamente aprovechar el proporcionado com.iconsolutions.instantpayments.csm. MapperRegistry y cree un Spring bean clase que implementa el com.iconsolutions.instantpayments.csm. MessageMapper<FromType, ToType> interfaz y contiene lo requerido mapping función:

import com.iconsolutions.instantpayments.csm. MessageMapper;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTResponse;
import com.myorg.myproject.mycsm.model. Header;
import com.iconsolutions.ipf.transformation. TransformationService;
import lombok. RequiredArgsConstructor;
import lombok.extern.slf4j. Slf4j;
import org.springframework.stereotype. Component;
import com.iconsolutions.iso20022.message.definitions.payments_clearing_and_settlement.pacs002. FIToFIPaymentStatusReportV10;
import mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_002_001_10. Document;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleResponse;

@Slf4j
@RequiredArgsConstructor
@Component
public class MyCsmToIpfPacs002Mapper implements MessageMapper<Document, MyCsmOutboundCTResponse> {

    private static final Version VERSION = new Version(1, 0, 0);

    private final TransformationService transformationService;

    @Override
    public Class<Document> supportedType() {
        return Document.class;
    }

    @Override
    public MyCsmOutboundCTResponse map(Document schemePaymentStatusReportDocument) {
        var schemePaymentStatusReport = schemePaymentStatusReportDocument.getFIToFIPmtStsRpt();
        log.debug("Mapping scheme PaymentStatusReport: {}", schemePaymentStatusReport);
        var isoPaymentStatusReport = transformationService.map(schemePaymentStatusReport, FIToFIPaymentStatusReportV10.class);
            return new MyCsmOutboundCTResponse( (1)
                    new Header(schemePaymentStatusReport.getOrgnlGrpInfAndSts().getOrgnlMsgId()),
                    ClearAndSettleResponse.builder()
                            .responseId(schemePaymentStatusReport.getTxInfAndSts().getOrgnlTxId())
                            .requestId(isoPaymentStatusReport.getGrpHdr().getMsgId())
                            .version(VERSION)
                            .createdAt(Instant.now())
                            .payload(new Payload<>(isoPaymentStatusReport, VERSION))
                            .status(status)
                            .build());
    }
}
1 Creando un nuevo MyCsmOutboundCTResponse, que es un custom objeto que le permite enviar un Header con el ClearAndSettleResponse en el ClearAndSettleSendConnector:
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleResponse;
import com.myorg.myproject.mycsm.model. Header;
import lombok. AllArgsConstructor;
import lombok. Data;

@Data
@AllArgsConstructor
public class MyCsmOutboundCTResponse {
    private Header header;
    private ClearAndSettleResponse payload;
}

Reuniendo todo esto, nuestro MyCsmMapper ahora se ve así:

import lombok.extern.slf4j. Slf4j;
import org.springframework.stereotype. Component;
import com.iconsolutions.instantpayments.csm. MapperRegistry;
import com.iconsolutions.mapper. JaxbObjectToStringMapper;
import org.springframework.core.io. Resource;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTResponse;
import com.iconsolutions.ipf.core.connector.message. TransportMessage;
import jakarta.xml.bind. JAXBContext;
import javax.xml.transform.stream. StreamSource;
import javax.xml.validation. SchemaFactory;
import com.iconsolutions.mapper. JaxbObjectToStringMapper;
import com.iconsolutions.mapper. StringToJaxbObjectMapper;
import jakarta.xml.bind. JAXBException;
import org.xml.sax. SAXException;
import java.io. IOException;

@Slf4j
@Component
public class MyCsmMapper {

    private final MapperRegistry mapperRegistry;
    private final JaxbObjectToStringMapper<Object> marshaller;
    private final StringToJaxbObjectMapper<Object> unmarshaller;


    public MyCsmMapper(@Value("classpath:xsd/mycsm_pacs_008.xsd") Resource myCsmPacs008Xsd,
                       @Value("classpath:xsd/mycsm_pacs_002.xsd") Resource myCsmPacs002Xsd,
                       final MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
        this.marshaller = marshaller();
        this.unmarshaller = unmarshaller();
    }

    public MyCsmOutboundCTRequest map(ClearAndSettleRequest clearAndSettleRequest) {
        return mapperRegistry.map(clearAndSettleRequest);
    }

    public TransportMessage convertToTransport(MyCsmOutboundCTRequest request) {
        var myCsmpacs008ObjectFactory = new mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. ObjectFactory();
        var document = myCsmpacs008ObjectFactory.createDocument();
        document.setFIToFICstmrCdtTrf(request.getPayload());
        var convertedMessage = marshaller.map(document);
        log.debug("Converted MyCsmOutboundCTRequest to transport message type: {}", convertedMessage);
        return new TransportMessage(convertedMessage);
    }

    public MyCsmOutboundCTResponse convertToResponse(TransportMessage transportMessage) {
        var messageText = message.getPayload().toString();
        log.debug("Converting to MyCsmOutboundCTResponse: {}", messageText);
        var schemePaymentStatusReportDocument = unmarshaller.map(messageText);
        return mapperRegistry.map(schemePaymentStatusReportDocument);
    }

    private JaxbObjectToStringMapper<Object> marshaller() {
        return new JaxbObjectToStringMapper<>(myCsmJAXBContext(), myCsmSchema(), false);
    }

    private StringToJaxbObjectMapper<Object> unmarshaller() {
        return new StringToJaxbObjectMapper<>(myCsmJAXBContext(), myCsmSchema());
    }

    private JAXBContext myCsmJAXBContext() throws JAXBException {
        return JAXBContext.newInstance(
            mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_008_001_08. Document.class,
            mycsm.scti.iso.std.iso._20022.tech.xsd.pacs_002_001_10. Document.class
        );
    }

    private Schema myCsmSchema() throws IOException, SAXException {
        StreamSource[] schemaStreamSources = {
                new StreamSource(myCsmPacs008Xsd.getInputStream()),
                new StreamSource(myCsmPacs002Xsd.getInputStream())
        };
        return SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(schemaStreamSources);
    }
}
Reenvíe ClearAndSettleResponse a IPF

Lo último que necesitamos crear para nuestro ReceiveConnector es un ReceiveHandler que será responsable de remitir el ClearAndSettleResponse mensaje a IPF. Usando el proporcionado ClearAndSettleResponseSender, la implementación más simple de un ReceiveHandler Spring Bean la clase puede parecer algo como esto:

import com.iconsolutions.instantpayments.csm.ct. ClearAndSettleResponseSender;
import com.iconsolutions.ipf.core.connector.api. ReceivingContext;
import com.iconsolutions.ipf.core.connector.receive.stages. ReceiveHandler;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleResponse;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTResponse;
import lombok. RequiredArgsConstructor;
import org.springframework.stereotype. Component;
import lombok.extern.slf4j. Slf4j;
import java.util.concurrent. CompletionStage;

@Slf4j
@Component
@RequiredArgsConstructor
public class MyCsmReceiveHandler implements ReceiveHandler<MyCsmOutboundCTResponse> { (1)

    private final ClearAndSettleResponseSender clearAndSettleResponseSender;

    @Override
    public CompletionStage<Void> handle(ReceivingContext receivingContext, MyCsmOutboundCTResponse myCsmOutboundCTResponse) {
        var payload = myCsmOutboundCTResponse.getPayload();
        log.debug("Handling clear and settle response with id: {}", payload.getResponseId());
        return clearAndSettleResponseSender.send(payload); (2)
    }
}
1 Implementando el ReceiveHandler interfaz que es utilizada por el ReceiveConnector para manejar los mensajes consumidos
2 Usando el proporcionado ClearAndSettleResponseSender para reenviar el ClearAndSettleResponse a IPF

Ahora que tenemos todos los componentes requeridos para el ReceiveConnector, podemos proceder a añadirlo a nuestra clase de configuración:

import akka.actor. ClassicActorSystemProvider;
import com.iconsolutions.ipf.core.connector. SendConnector;
import com.iconsolutions.ipf.core.connector.transport. ConnectorTransport;
import com.iconsolutions.ipf.core.connector.transport. ReceivingConnectorTransport;
import com.iconsolutions.ipf.core.messagelogger. MessageLogger;
import com.iconsolutions.ipf.core.shared.correlation. CorrelationId;
import com.iconsolutions.ipf.core.shared.correlation. CorrelationService;
import com.iconsolutions.ipf.payments.api.csm.clearandsettle.api. ClearAndSettleRequest;
import com.myorg.myproject.mycsm.mapper.ct. MyCsmMapper;
import com.myorg.myproject.mycsm.mapper.ct. MyCsmReceiveHandler;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTRequest;
import com.myorg.myproject.mycsm.model.ct. MyCsmOutboundCTResponse;
import org.springframework.boot.autoconfigure. AutoConfiguration;
import org.springframework.context.annotation. Bean;

@AutoConfiguration
public class MyCsmConfig {

    private final ClassicActorSystemProvider actorSystem;
    private final CorrelationService correlationService;
    private final MessageLogger messageLogger;

    public MyCsmConfig(ClassicActorSystemProvider actorSystem,
                       CorrelationService correlationService,
                       MessageLogger messageLogger) {
        this.actorSystem = actorSystem;
        this.correlationService = correlationService;
        this.messageLogger = messageLogger;
    }

    @Bean
    SendConnector<ClearAndSettleRequest, MyCsmOutboundCTRequest> clearAndSettleRequestSendConnector(MyCsmMapper mapper,
                                                                                                    ConnectorTransport<MyCsmOutboundCTRequest> transport) {
        String connectorName = "myCsmClearAndSettleRequestSender";
        String configRoot = "my-csm.send-cas-connector";
        return SendConnector.<ClearAndSettleRequest, MyCsmOutboundCTRequest>builder(connectorName, configRoot, actorSystem)
                .withMessageLogger(messageLogger)
                .withCorrelationService(correlationService)
                .withConnectorTransport(transport)
                .withDomainToTargetTypeConverter(clearAndSettleRequest -> mapper.map(clearAndSettleRequest)) (1)
                .withSendTransportMessageConverter(outboundRequest -> mapper.convertToTransport(outboundRequest)) (2)
                .withCorrelationIdExtractor(outboundRequest -> CorrelationId.of(outboundRequest.getPayload().getGrpHdr().getMsgId()))
                .build();
    }

    @Bean
    ReceiveConnector<MyCsmOutboundCTResponse> csmAdapterReceiveConnector(MyCsmMapper mapper,
                                                                         MyCsmReceiveHandler receiveHandler,
                                                                         ReceivingConnectorTransport transport) {
        String connectorName = "myCsmReceiver";
        String configRoot = "my-csm.receive-connector";
        return ReceiveConnector.<MyCsmOutboundCTResponse>builder(connectorName, configRoot, actorSystem)
                .withMessageLogger(messageLogger)
                .withCorrelationService(correlationService)
                .withCorrelationIdExtractor(response -> CorrelationId.of(response.getHeader().getAUsefulHeaderId()))
                .withConnectorTransport(transport)
                .withReceiveTransportMessageConverter(transportMessage -> mapper.convertToResponse(transportMessage))
                .withReceiveHandler(receiveHandler)
                .build();
    }
}
Por favor, visite el Conector de Recepción página si no está familiarizado con ninguna de las otras opciones de ReceiveConnector proporcionadas arriba.

Después de crear este ReceiveConnector, ahora tenemos todos los componentes y la lógica requeridos para implementar esta parte de la Flujo de Transferencia de Crédito del Deudor:

debtor-payment-csm-flow

Los pasos que hemos seguido para implementar este flujo de trabajo pueden utilizarse posteriormente para implementar cualquiera de los otros soportado CSM flujos de trabajo usted quiere incluir en su csm service. En resumen, este proceso implica:

  • Implementando el método(s) de recibir mensaje apropiado(s) en el proporcionado CsmApiReceiver/CsmRApiReceiver interfaces

  • Creando conectores para reenviar mensajes a/recibir mensajes de la CSM, y transformando los mensajes según sea necesario

  • Utilizando los remitentes proporcionados (por ejemplo,ClearAndSettleResponseSender) para reenviar mensajes desde el CSM a IPF

Apéndice A:CSM Referencia de Funciones

Como se describe en el [Steps] sección,`csm-service-starter- * ` proporciona un conjunto de conectores listos para usar y transportes asociados que facilitan el intercambio de CSM mensajes específicos entre el CSM Service y IPF para todos soportado CSM flujos de trabajo. La tabla a continuación resume las propiedades de configuración que se pueden utilizar para habilitar/deshabilitar estos conectores, permitiéndole personalizar su CSM Service conjunto de características a sus requisitos específicos. Por defecto, todas las CSM las características a continuación están habilitadas.

  1. CSM Service Características

Característica

Propiedad

Descripción

Valor por defecto

Aclarar y Liquidar Deudor

csm.clear-and-settle.debtor.enabled

Habilita los conectores de envío y recepción asociados con el flujo de trabajo de liquidación y compensación del deudor.

true

Aclarar y Liquidar Acreedor

csm.clear-and-settle.creditor.enabled

Habilita los conectores de envío y recepción asociados con el flujo de trabajo de compensación y liquidación del acreedor.

true

Aclarar y Resolver Técnico

csm.clear-and-settle.technical.enabled

Habilita la respuesta técnica clara y resuelta. SendConnector.

true

Aclarar y Liquidar Notifications

csm.clear-and-settle.notification.enabled

Habilita los SendConnectors para claro y liquidar notifications.

true

Solicitud de Estado

csm.status-request.enabled

Habilita los conectores de envío y recepción para solicitudes de estado.

true

Recibir Pago

csm.receive-payment.enabled

Habilita recibir pagos y recibir pagos liquidados SendConnectors.csm.clear-and-settle.creditor.enabled también debe ser configurado a true para que esto sea habilitado.

true

Resultado de la Investigación/Devolución/Recuperación Deudor

csm.rrr.debtor.enabled

Habilita los conectores de envío y recepción asociados con los flujos de trabajo de deudores RRR.

true

Resultado de la Investigación/Devolución/Recuperación Acreedor

csm.rrr.creditor.enabled

Habilita los conectores de envío y recepción asociados con los flujos de trabajo de RRR del acreedor.

true

Débito Directo Acreedor

csm.direct-debit.creditor.enabled

Habilita los conectores de envío y recepción asociados con los flujos de trabajo de débito directo del acreedor (débito directo, cancelación, reverso).

false

Débito Directo Técnico

csm.direct-debit.technical.enabled

Habilita la respuesta técnica de domiciliación bancaria. SendConnector.

false

Apéndice B: Anulación de predeterminado CSM Service configuración

Usando el referencia de transporte página como guía, los siguientes ejemplos demuestran cómo anular las ubicaciones predeterminadas que los conectores proporcionaron en `csm-service-starter- * ` consuma de y produzca a.

Kafka

csm.kafka {

  producer {
    topics {
      clear-and-settle {
        csm-to-debtor = CLEARANDSETTLE_CSM_TO_DEBTOR //csm-service sends
        csm-to-creditor = CLEARANDSETTLE_CSM_TO_CREDITOR //csm-service sends
        technical-response = CLEARANDSETTLE_TECHNICAL_RESPONSE //csm-service sends
      }
      rrr {
        csm-to-debtor = RRR_CSM_TO_DEBTOR //csm-service sends
        csm-to-creditor = RRR_CSM_TO_CREDITOR //csm-service sends
        technical-response = RRR_TECHNICAL_RESPONSE //csm-service sends
      }
      direct-debit {
        technical-response = DIRECTDEBIT_TECHNICAL_RESPONSE //service sends
        csm-to-creditor = DIRECTDEBIT_CSM_TO_CREDITOR //service sends
      }
      notifications = CSM_NOTIFICATIONS //csm-service sends
    }
  }

  consumer {
    kafka-clients {
      group.id = csm-client-group
    }
    topics {
      clear-and-settle {
        debtor-to-csm = CLEARANDSETTLE_DEBTOR_TO_CSM //csm-service receives
        creditor-to-csm = CLEARANDSETTLE_CREDITOR_TO_CSM //csm-service receives
      }
      rrr {
        debtor-to-csm = RRR_DEBTOR_TO_CSM //csm-service receives
        creditor-to-csm = RRR_CREDITOR_TO_CSM //csm-service receives
      }
      direct-debit {
        creditor-to-csm = DIRECTDEBIT_CREDITOR_TO_CSM //csm-service receives
      }
    }
  }
}

JMS [source,hocon]

csm.jms {
  clear-and-settle {
    debtor-to-csm.queue = clearandsettle.debtor.to.csm //csm-service receives
    creditor-to-csm.queue = clearandsettle.creditor.to.csm //csm-service receives
    csm-to-debtor.queue = clearandsettle.csm.to.debtor //csm-service sends
    csm-to-creditor.queue = clearandsettle.csm.to.creditor //csm-service sends
    technical-response.queue = clearandsettle.technical.response //csm-service sends
  }
  rrr {
    debtor-to-csm.queue = rrr.debtor.to.csm //csm-service receives
    creditor-to-csm.queue = rrr.creditor.to.csm //csm-service receives
    csm-to-debtor.queue = rrr.csm.to.debtor //csm-service sends
    csm-to-creditor.queue = rrr.csm.to.creditor //csm-service sends
    technical-response.queue = rrr.technical.response //csm-service sends
  }
  direct-debit {
    technical-response.queue = directdebit.technical.response //csm-service sends
    creditor-to-csm.queue = directdebit.creditor.to.csm //csm-service receives
    csm-to-creditor.queue = directdebit.csm.to.creditor //csm-service sends
  }
  notifications.queue = csm.notifications //csm-service sends
}

Apéndice C: Adjuntos de cartas muertas

`csm-service-starter- * `proporciona dos DeadLetterAppender interfaces para manejar los mensajes recibidos que fallan durante el procesamiento:

  • com.iconsolutions.instantpayments.csm.ct. CsmCTDeadLetterAppenders: para la transferencia de crédito ReceiveConnectors

  • com.iconsolutions.instantpayments.csm.rrr. CsmRMessageDeadLetterAppenders: para la recuperación/devolución/resultado de la investigación ReceiveConnectors