soutenance de stage

Ingénieur études et développement - progiciel monétique

NGUYEN Clément - INFO5

2023

> Entreprise : Sopra Banking Software

 

> Objectif : Développement de nouvelles fonctionnalités sur des progiciels monétique

 

> Participation au développement de RTSI (Real-Time Systems Inferface)

INTRODUCTION

PLAN

- Filiale du groupe Sopra Steria

- Spécialisée dans le secteur bancaire

- Agence "Cards" : département R&D monétique

- Monétique : Front and Back office

1. SOPRA banking software

Contexte RTSI (Real-Time Systems Interface) :

Visa a crée un système en ligne nommée VROL

 > permet d'échanger des documents liés à des litiges

 

Application RTSI

> permet de déclarer des litiges auprès de VROL

 

Temps de développement : 2 mois

2.1. Présentation de RTSI

Text

2.2. architecture du service

1. Organisation de l'équipe

 

2. Les technologies utilisées

3. l'environnement de travail

Méthode Agile

- Agile Manager

- Scrum Master

- Product Owner

- Developers

 

1) Sprint planning

>> Product backlog

>> Grooming

3.1. l'organisation de l'équipe

2) Sprint

>> 2 semaines

 

3) Sprint Review

>> Démonstration

 

4) Rétrospective

>> Ce qu'on a aimé

>> Ce qu'on a appris

>> Ce qui a manqué

>> Les actions à faire

3.2. les technologies utilisées

Architecture en microservices

 

> Spring boot : framework pour applications Java

- Automatise la configuration

- Automatise la mise en place d'applications

- Gestion de dépendances

 

> On gagne en productivité

3.2. les technologies utilisées

Programmation réactive

> Approche de programmation pour gérer les flux de données et les événements.

> Réagit rapidement et de manière asynchrone aux changements.

 

Reactor

> Bibliothèque de programmation réactive.

> Gère les flux de données et les événements de manière asynchrone.

1. Processus de RTSI

 

2. Développement avec un scénario d'exemple

 

3. Les tests

4. le développement de rtsi

4. 1. processus de rtsi

4.1. processus de rtsi

Scénario d'exemple :

 

Appel n°1 : SubmitTranInquiry

> Récupérer informations de la transaction

 

Appel n°2 : InitiateDisputeFromTransaction

> Initialisation du dossier de litige

 

Si FraudRptInd = "NotExist"

> Exception

- FraudRptInd

4.2. Le développement

Etape 1 : Configuration du JMS pour se connecter à ActiveMQ

@Configuration
@EnableJms
public class JmsConfiguration implements JmsListenerConfigurer {

    private final ActiveMQProperties activeMqProperties;

    public JmsConfiguration(ActiveMQProperties activeMqProperties) {
        this.activeMqProperties = activeMqProperties;
    }

    @Bean
    public ConnectionFactory activeMQConnectionFactory() {
        ActiveMQConnectionFactory connectionFactory =
                new ActiveMQConnectionFactory(activeMqProperties.credentials().userId(),
                                              activeMqProperties.credentials().password(),
                                              activeMqProperties.brokerUrl());
        connectionFactory.setTrustedPackages(List.of(DXP_BASE_PACKAGE));
        return connectionFactory;
    }
}

> communication entre applications

4.2. Le développement

Etape 2 : Configuration du clientDto

public record TranInquiryRequestClientDto(
        @JsonProperty("RequestHeader")
        RequestHeaderClientDto requestHeader,
        @Valid
        @JsonProperty("RequestData")
        RequestDataClientDto requestData
) {}
public record RequestHeaderClientDto(
        @JsonProperty("User")
        UserClientDto user,
        @JsonProperty("MemberRole")
        MemberRole memberRole
) {}
public record RequestDataClientDto(
        @JsonProperty("TransactionID")
        String transactionID
) {}

> création de l'objet de la requête pour appeler /SubmitTranInquiry

user, memberRole et transactionID nécessaires pour appeler l'API

4.2. Le développement

Etape 3 : Création du mapper

        public static TranInquiryRequestClientDto toRequestClientDto(String input, CompanyCodesProperties properties) {
            CompanyCodesProperties.CompanyCodeProperties property = getCompanyCodeFromProperties(input, properties);
            return new TranInquiryRequestClientDto(
                    new RequestHeaderClientDto(new UserClientDto(property.userId(),
                                                                 property.userType()),
                                               toMemberRole(property)),
                    new RequestDataClientDto(getFieldValue("IdTransactionUniq", input))
            );
        }

> convertit des données d'un format en un autre

<message>
  <entete>
    <codReg>13001</codReg>
    <codMessage>string</codMessage>
  </entete>
  <body>
    <refIntOperation>string</refIntOperation>
    <idTransactionUniq>1010000</idTransactionUniq>
    <mntImpayeComp>4934200</mntImpayeComp>
    <codDevMntBrutComp>840</codDevMntBrutComp>
  </body>
</message>

transactionID = 1010000

4.2. Le développement

Etape 4 : Création du consumer

> écouter et consommer les messages provenant de la queue

    @JmsListener(destination = "${queues.queueChargebackRequest}")
    public void processIssuerMessage(String message) {
        Mono.just(message)
            .doOnSubscribe(m -> logger.debug("Received message from Issuer: {}", message))
            .filter(xmlMessageService::isMessageValid)
            .flatMap(visaService::processVisaStages)
            .flatMap(r -> sendResponse(toXml(r)))
            .onErrorResume(Exception.class, e -> sendResponse(toXml(toResponse(message, emptyList(), e))))
            .subscribe();
    }

isMessageValid() : Vérification du message XML

processVisaStages() : Traitement du message

sendResponse() : Envoie d'un message retour dans une autre queue

4.2. Le développement

Etape 5 : Traitement du message (scénario d'exemple)

    public Mono<Response> processVisaStages(String issuerMsg) {
        Map<VisaApi, Object> mapResponses = new EnumMap<>(VisaApi.class);
        List<StepDone> steps = new ArrayList<>();
        return Mono.just(mapResponses)
                   .flatMap(r -> checkIfChargebackIsNotAFraud(r, issuerMsg))
                   .flatMap(r -> processStageSubmitTranInquiry(issuerMsg, r, steps))
                   .flatMap(r -> processStageInitiateDisputeFromTransactionOrCase(issuerMsg, r, steps))
                   .flatMap(this::stopProcessWhenFraudReportNotExist)
                   .flatMap(r -> continueProcessIfFraudReportExist(issuerMsg, r, steps))
                   .map(r -> toResponse(issuerMsg, steps, null))
                   .onErrorResume(Exception.class, e -> Mono.just(toResponse(issuerMsg, steps, e)));
    }

1

2

3

4.2. Le développement

Etape 6 : Préparation de la requête et appel de l'API SubmitTranInquiry

private Mono<Map> processStageSubmitTranInquiry(String issuerMsg,
                                                Map<VisaApi, Object> mapResponses,
                                                List<StepDone> steps) {
	return Mono.just(toRequestClientDto(issuerMsg, companyCodesProperties))
               .map(validatorService::validate)
               .flatMap(r -> submitTranInquiryService.submitTranInquiry(r, issuerMsg, steps))
               .map(r -> putObjectInMap(mapResponses, SUBMIT_TRAN_INQUIRY, r));
}

public Mono<TranInquiryResponseClientDto> submitTranInquiry(
            TranInquiryRequestClientDto request
    ) {
        String requestId = String.valueOf(UUID.randomUUID());
        return webClient.post()
                        .uri(API.getUri())
                        .bodyValue(request)
                        .retrieve()
                        .toEntity(TranInquiryResponseClientDto.class)
                        .doOnSubscribe(s -> logger.info(LOG_REQUEST, API.getUri(), requestId, toMaskedJson(request)))
                        .doOnNext(
                                r -> logger.info(LOG_RESPONSE, API.getUri(), requestId, r.getStatusCodeValue(), toMaskedJson(r.getBody())))
                        .map(HttpEntity::getBody);
    }

URI = /submitTranInquiry

4.3. Les tests

Etape 1 : Création des mocks

> Pas accès aux APIs de Visa

> Mockito : simuler les appels à Visa

Comment simuler l'appel de la première API ?

{
  "request": {
    "method": "POST",
    "urlPathPattern": "/rsrv_rolti-mte2/api/submitTranInquiry",
    "bodyPatterns": [
      {
        "matchesJsonPath": "$.RequestData[?(@.TransactionID == '1000000200')]"
      }
    ]
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "jsonBody": {
      "Status": [
        {
          "Code": "I-300000000",
          "Message": "Successfully completed Operation."
        }
      ],
      "ResponseData": {
        "TIEventID": 27855353,
        "TransactionSummary": [
          {
            "RolTransactionId": 172632355,
            "TransactionType": "TC05"
          }
        ]
      }
    }
  }
}

4.3. Les tests

Etape 2 : Tests unitaires

> Testons la fonction précédemment démontrée

    @Test
    void submitTranInquiry_shouldSucceed_whenStatus200() {
        TranInquiryRequestClientDto request = datasetTranInquiryRequest("1000000200");
        String issuerMessage = datasetXml("13001", "1000000200");
        test(service.submitTranInquiry(request, issuerMessage, new ArrayList<>()))
                .expectSubscription()
                .consumeNextWith(r -> {
                    assertThat(r).isNotNull();
                    assertThat(r.status().get(0).code()).isEqualTo("I-300000000");
                    assertThat(r.responseData().tiEventID()).isEqualTo(27855353);
                    assertThat(r.responseData().transactionSummary().get(0).rolTransactionId()).isEqualTo(172632355);
                })
                .verifyComplete();
    }

Idée de carrière

 

Amélioration de compétences techniques et transversales

 

Mon futur dans l'entreprise

5. bilan du stage

Merci

Avez-vous des questions ?

SoutenanceStageINFO5-2023-NGUYEN-Clément

By Clément Nguyen

SoutenanceStageINFO5-2023-NGUYEN-Clément

  • 61