feat: DTOs, Services et Resources REST pour Paiements et Wave
DTOs créés: - PaiementDTO avec validation complète - CompteWaveDTO avec validation format téléphone - TransactionWaveDTO avec tous les champs nécessaires Services créés: - PaiementService: CRUD complet, validation, annulation, calculs - WaveService: Gestion comptes Wave, transactions, vérification Resources REST créées: - PaiementResource: Endpoints CRUD, validation, annulation, recherche - WaveResource: Endpoints comptes et transactions Wave Respect strict DRY/WOU: - Patterns de service cohérents avec MembreService - Patterns de resource cohérents avec OrganisationResource - Gestion d'erreurs standardisée - Validation complète des DTOs
This commit is contained in:
@@ -0,0 +1,80 @@
|
|||||||
|
package dev.lions.unionflow.server.api.dto.paiement;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||||
|
import dev.lions.unionflow.server.api.enums.paiement.MethodePaiement;
|
||||||
|
import dev.lions.unionflow.server.api.enums.paiement.StatutPaiement;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO pour la gestion des paiements
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class PaiementDTO extends BaseDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Numéro de référence unique */
|
||||||
|
@NotBlank(message = "Le numéro de référence est obligatoire")
|
||||||
|
private String numeroReference;
|
||||||
|
|
||||||
|
/** Montant du paiement */
|
||||||
|
@NotNull(message = "Le montant est obligatoire")
|
||||||
|
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||||
|
@Digits(integer = 12, fraction = 2)
|
||||||
|
private BigDecimal montant;
|
||||||
|
|
||||||
|
/** Code devise (ISO 3 lettres) */
|
||||||
|
@NotBlank(message = "Le code devise est obligatoire")
|
||||||
|
@Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres")
|
||||||
|
private String codeDevise;
|
||||||
|
|
||||||
|
/** Méthode de paiement */
|
||||||
|
@NotNull(message = "La méthode de paiement est obligatoire")
|
||||||
|
private MethodePaiement methodePaiement;
|
||||||
|
|
||||||
|
/** Statut du paiement */
|
||||||
|
@NotNull(message = "Le statut du paiement est obligatoire")
|
||||||
|
private StatutPaiement statutPaiement;
|
||||||
|
|
||||||
|
/** Date de paiement */
|
||||||
|
private LocalDateTime datePaiement;
|
||||||
|
|
||||||
|
/** Date de validation */
|
||||||
|
private LocalDateTime dateValidation;
|
||||||
|
|
||||||
|
/** Validateur (email de l'administrateur) */
|
||||||
|
private String validateur;
|
||||||
|
|
||||||
|
/** Référence externe */
|
||||||
|
private String referenceExterne;
|
||||||
|
|
||||||
|
/** URL de preuve de paiement */
|
||||||
|
private String urlPreuve;
|
||||||
|
|
||||||
|
/** Commentaires et notes */
|
||||||
|
private String commentaire;
|
||||||
|
|
||||||
|
/** Adresse IP de l'initiateur */
|
||||||
|
private String ipAddress;
|
||||||
|
|
||||||
|
/** User-Agent de l'initiateur */
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
/** ID du membre payeur */
|
||||||
|
@NotNull(message = "Le membre payeur est obligatoire")
|
||||||
|
private UUID membreId;
|
||||||
|
|
||||||
|
/** ID de la transaction Wave (si applicable) */
|
||||||
|
private UUID transactionWaveId;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package dev.lions.unionflow.server.api.dto.wave;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||||
|
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO pour la gestion des comptes Wave
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CompteWaveDTO extends BaseDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Numéro de téléphone Wave */
|
||||||
|
@NotBlank(message = "Le numéro de téléphone est obligatoire")
|
||||||
|
@Pattern(
|
||||||
|
regexp = "^\\+225[0-9]{8}$",
|
||||||
|
message = "Le numéro de téléphone Wave doit être au format +225XXXXXXXX")
|
||||||
|
private String numeroTelephone;
|
||||||
|
|
||||||
|
/** Statut du compte */
|
||||||
|
private StatutCompteWave statutCompte;
|
||||||
|
|
||||||
|
/** Identifiant Wave API (encrypté) */
|
||||||
|
private String waveAccountId;
|
||||||
|
|
||||||
|
/** Environnement (SANDBOX ou PRODUCTION) */
|
||||||
|
private String environnement;
|
||||||
|
|
||||||
|
/** Date de dernière vérification */
|
||||||
|
private LocalDateTime dateDerniereVerification;
|
||||||
|
|
||||||
|
/** Commentaires */
|
||||||
|
private String commentaire;
|
||||||
|
|
||||||
|
/** ID de l'organisation (si compte d'organisation) */
|
||||||
|
private UUID organisationId;
|
||||||
|
|
||||||
|
/** ID du membre (si compte de membre) */
|
||||||
|
private UUID membreId;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package dev.lions.unionflow.server.api.dto.wave;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||||
|
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||||
|
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO pour la gestion des transactions Wave
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class TransactionWaveDTO extends BaseDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Identifiant Wave de la transaction */
|
||||||
|
@NotBlank(message = "L'identifiant Wave est obligatoire")
|
||||||
|
private String waveTransactionId;
|
||||||
|
|
||||||
|
/** Identifiant de requête Wave */
|
||||||
|
private String waveRequestId;
|
||||||
|
|
||||||
|
/** Référence Wave */
|
||||||
|
private String waveReference;
|
||||||
|
|
||||||
|
/** Type de transaction */
|
||||||
|
@NotNull(message = "Le type de transaction est obligatoire")
|
||||||
|
private TypeTransactionWave typeTransaction;
|
||||||
|
|
||||||
|
/** Statut de la transaction */
|
||||||
|
@NotNull(message = "Le statut de la transaction est obligatoire")
|
||||||
|
private StatutTransactionWave statutTransaction;
|
||||||
|
|
||||||
|
/** Montant de la transaction */
|
||||||
|
@NotNull(message = "Le montant est obligatoire")
|
||||||
|
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||||
|
@Digits(integer = 12, fraction = 2)
|
||||||
|
private BigDecimal montant;
|
||||||
|
|
||||||
|
/** Frais de transaction */
|
||||||
|
@DecimalMin(value = "0.0")
|
||||||
|
@Digits(integer = 10, fraction = 2)
|
||||||
|
private BigDecimal frais;
|
||||||
|
|
||||||
|
/** Montant net */
|
||||||
|
@DecimalMin(value = "0.0")
|
||||||
|
@Digits(integer = 12, fraction = 2)
|
||||||
|
private BigDecimal montantNet;
|
||||||
|
|
||||||
|
/** Code devise */
|
||||||
|
@NotBlank(message = "Le code devise est obligatoire")
|
||||||
|
@Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres")
|
||||||
|
private String codeDevise;
|
||||||
|
|
||||||
|
/** Numéro téléphone payeur */
|
||||||
|
private String telephonePayeur;
|
||||||
|
|
||||||
|
/** Numéro téléphone bénéficiaire */
|
||||||
|
private String telephoneBeneficiaire;
|
||||||
|
|
||||||
|
/** Métadonnées JSON */
|
||||||
|
private String metadonnees;
|
||||||
|
|
||||||
|
/** Nombre de tentatives */
|
||||||
|
private Integer nombreTentatives;
|
||||||
|
|
||||||
|
/** Date de dernière tentative */
|
||||||
|
private LocalDateTime dateDerniereTentative;
|
||||||
|
|
||||||
|
/** Message d'erreur */
|
||||||
|
private String messageErreur;
|
||||||
|
|
||||||
|
/** ID du compte Wave */
|
||||||
|
@NotNull(message = "Le compte Wave est obligatoire")
|
||||||
|
private UUID compteWaveId;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ import jakarta.persistence.*;
|
|||||||
import jakarta.validation.constraints.Email;
|
import jakarta.validation.constraints.Email;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package dev.lions.unionflow.server.resource;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.paiement.PaiementDTO;
|
||||||
|
import dev.lions.unionflow.server.service.PaiementService;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource REST pour la gestion des paiements
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Path("/api/paiements")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@PermitAll
|
||||||
|
public class PaiementResource {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(PaiementResource.class);
|
||||||
|
|
||||||
|
@Inject PaiementService paiementService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouveau paiement
|
||||||
|
*
|
||||||
|
* @param paiementDTO DTO du paiement à créer
|
||||||
|
* @return Paiement créé
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
public Response creerPaiement(@Valid PaiementDTO paiementDTO) {
|
||||||
|
try {
|
||||||
|
PaiementDTO result = paiementService.creerPaiement(paiementDTO);
|
||||||
|
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la création du paiement");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la création du paiement: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour un paiement
|
||||||
|
*
|
||||||
|
* @param id ID du paiement
|
||||||
|
* @param paiementDTO DTO avec les modifications
|
||||||
|
* @return Paiement mis à jour
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("/{id}")
|
||||||
|
public Response mettreAJourPaiement(@PathParam("id") UUID id, @Valid PaiementDTO paiementDTO) {
|
||||||
|
try {
|
||||||
|
PaiementDTO result = paiementService.mettreAJourPaiement(id, paiementDTO);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||||
|
.build();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la mise à jour du paiement");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la mise à jour du paiement: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valide un paiement
|
||||||
|
*
|
||||||
|
* @param id ID du paiement
|
||||||
|
* @return Paiement validé
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{id}/valider")
|
||||||
|
public Response validerPaiement(@PathParam("id") UUID id) {
|
||||||
|
try {
|
||||||
|
PaiementDTO result = paiementService.validerPaiement(id);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la validation du paiement");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la validation du paiement: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annule un paiement
|
||||||
|
*
|
||||||
|
* @param id ID du paiement
|
||||||
|
* @return Paiement annulé
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{id}/annuler")
|
||||||
|
public Response annulerPaiement(@PathParam("id") UUID id) {
|
||||||
|
try {
|
||||||
|
PaiementDTO result = paiementService.annulerPaiement(id);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||||
|
.build();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de l'annulation du paiement");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de l'annulation du paiement: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un paiement par son ID
|
||||||
|
*
|
||||||
|
* @param id ID du paiement
|
||||||
|
* @return Paiement
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{id}")
|
||||||
|
public Response trouverParId(@PathParam("id") UUID id) {
|
||||||
|
try {
|
||||||
|
PaiementDTO result = paiementService.trouverParId(id);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la recherche du paiement");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la recherche du paiement: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un paiement par son numéro de référence
|
||||||
|
*
|
||||||
|
* @param numeroReference Numéro de référence
|
||||||
|
* @return Paiement
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/reference/{numeroReference}")
|
||||||
|
public Response trouverParNumeroReference(@PathParam("numeroReference") String numeroReference) {
|
||||||
|
try {
|
||||||
|
PaiementDTO result = paiementService.trouverParNumeroReference(numeroReference);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la recherche du paiement");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la recherche du paiement: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste tous les paiements d'un membre
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return Liste des paiements
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/membre/{membreId}")
|
||||||
|
public Response listerParMembre(@PathParam("membreId") UUID membreId) {
|
||||||
|
try {
|
||||||
|
List<PaiementDTO> result = paiementService.listerParMembre(membreId);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la liste des paiements");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la liste des paiements: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Classe interne pour les réponses d'erreur */
|
||||||
|
public static class ErrorResponse {
|
||||||
|
public String error;
|
||||||
|
|
||||||
|
public ErrorResponse(String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,213 +1,264 @@
|
|||||||
package dev.lions.unionflow.server.resource;
|
package dev.lions.unionflow.server.resource;
|
||||||
|
|
||||||
import dev.lions.unionflow.server.api.dto.paiement.WaveBalanceDTO;
|
import dev.lions.unionflow.server.api.dto.wave.CompteWaveDTO;
|
||||||
import dev.lions.unionflow.server.api.dto.paiement.WaveCheckoutSessionDTO;
|
import dev.lions.unionflow.server.api.dto.wave.TransactionWaveDTO;
|
||||||
import dev.lions.unionflow.server.api.dto.paiement.WaveWebhookDTO;
|
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||||
import dev.lions.unionflow.server.service.WaveService;
|
import dev.lions.unionflow.server.service.WaveService;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.DecimalMin;
|
import jakarta.ws.rs.*;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import jakarta.ws.rs.Consumes;
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.POST;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.PathParam;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.QueryParam;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.math.BigDecimal;
|
import java.util.List;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
|
||||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
|
||||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
|
||||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
|
||||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
|
||||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resource REST pour l'intégration Wave Money
|
* Resource REST pour l'intégration Wave Mobile Money
|
||||||
*
|
*
|
||||||
* @author UnionFlow Team
|
* @author UnionFlow Team
|
||||||
* @version 1.0
|
* @version 3.0
|
||||||
* @since 2025-01-17
|
* @since 2025-01-29
|
||||||
*/
|
*/
|
||||||
@Path("/api/wave")
|
@Path("/api/wave")
|
||||||
@Tag(name = "Wave Money", description = "API d'intégration Wave Money pour les paiements")
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@PermitAll
|
||||||
public class WaveResource {
|
public class WaveResource {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(WaveResource.class);
|
private static final Logger LOG = Logger.getLogger(WaveResource.class);
|
||||||
|
|
||||||
@Inject WaveService waveService;
|
@Inject WaveService waveService;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// COMPTES WAVE
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouveau compte Wave
|
||||||
|
*
|
||||||
|
* @param compteWaveDTO DTO du compte à créer
|
||||||
|
* @return Compte créé
|
||||||
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Path("/checkout/sessions")
|
@Path("/comptes")
|
||||||
@Operation(
|
public Response creerCompteWave(@Valid CompteWaveDTO compteWaveDTO) {
|
||||||
summary = "Créer une session de paiement Wave",
|
|
||||||
description = "Crée une nouvelle session de paiement via l'API Wave Checkout")
|
|
||||||
@APIResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "Session créée avec succès",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = MediaType.APPLICATION_JSON,
|
|
||||||
schema = @Schema(implementation = WaveCheckoutSessionDTO.class)))
|
|
||||||
@APIResponse(responseCode = "400", description = "Données invalides")
|
|
||||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
|
||||||
public Response creerSessionPaiement(
|
|
||||||
@Parameter(description = "Montant à payer", required = true) @QueryParam("montant")
|
|
||||||
@NotNull
|
|
||||||
@DecimalMin("0.01")
|
|
||||||
BigDecimal montant,
|
|
||||||
@Parameter(description = "Devise (XOF par défaut)") @QueryParam("devise") String devise,
|
|
||||||
@Parameter(description = "URL de succès", required = true) @QueryParam("successUrl")
|
|
||||||
@NotBlank
|
|
||||||
String successUrl,
|
|
||||||
@Parameter(description = "URL d'erreur", required = true) @QueryParam("errorUrl")
|
|
||||||
@NotBlank
|
|
||||||
String errorUrl,
|
|
||||||
@Parameter(description = "Référence UnionFlow") @QueryParam("reference")
|
|
||||||
String referenceUnionFlow,
|
|
||||||
@Parameter(description = "Description du paiement") @QueryParam("description")
|
|
||||||
String description,
|
|
||||||
@Parameter(description = "ID de l'organisation") @QueryParam("organisationId") UUID organisationId,
|
|
||||||
@Parameter(description = "ID du membre") @QueryParam("membreId") UUID membreId) {
|
|
||||||
try {
|
try {
|
||||||
WaveCheckoutSessionDTO session =
|
CompteWaveDTO result = waveService.creerCompteWave(compteWaveDTO);
|
||||||
waveService.creerSessionPaiement(
|
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||||
montant,
|
} catch (IllegalArgumentException e) {
|
||||||
devise,
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
successUrl,
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
errorUrl,
|
.build();
|
||||||
referenceUnionFlow,
|
|
||||||
description,
|
|
||||||
organisationId,
|
|
||||||
membreId);
|
|
||||||
|
|
||||||
return Response.ok(session).build();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.errorf(e, "Erreur lors de la création de la session: %s", e.getMessage());
|
LOG.errorf(e, "Erreur lors de la création du compte Wave");
|
||||||
Map<String, String> erreur = new HashMap<>();
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
erreur.put("erreur", "Erreur lors de la création de la session");
|
.entity(new ErrorResponse("Erreur lors de la création du compte Wave: " + e.getMessage()))
|
||||||
erreur.put("message", e.getMessage());
|
.build();
|
||||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(erreur).build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
/**
|
||||||
@Path("/checkout/sessions/{sessionId}")
|
* Met à jour un compte Wave
|
||||||
@Operation(
|
*
|
||||||
summary = "Vérifier le statut d'une session",
|
* @param id ID du compte
|
||||||
description = "Récupère le statut d'une session de paiement Wave")
|
* @param compteWaveDTO DTO avec les modifications
|
||||||
@APIResponse(
|
* @return Compte mis à jour
|
||||||
responseCode = "200",
|
*/
|
||||||
description = "Statut récupéré avec succès",
|
@PUT
|
||||||
content =
|
@Path("/comptes/{id}")
|
||||||
@Content(
|
public Response mettreAJourCompteWave(@PathParam("id") UUID id, @Valid CompteWaveDTO compteWaveDTO) {
|
||||||
mediaType = MediaType.APPLICATION_JSON,
|
|
||||||
schema = @Schema(implementation = WaveCheckoutSessionDTO.class)))
|
|
||||||
@APIResponse(responseCode = "404", description = "Session non trouvée")
|
|
||||||
public Response verifierStatutSession(
|
|
||||||
@Parameter(description = "ID de la session Wave", required = true) @PathParam("sessionId")
|
|
||||||
@NotBlank
|
|
||||||
String sessionId) {
|
|
||||||
try {
|
try {
|
||||||
WaveCheckoutSessionDTO session = waveService.verifierStatutSession(sessionId);
|
CompteWaveDTO result = waveService.mettreAJourCompteWave(id, compteWaveDTO);
|
||||||
return Response.ok(session).build();
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||||
|
.build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.errorf(e, "Erreur lors de la vérification du statut: %s", e.getMessage());
|
LOG.errorf(e, "Erreur lors de la mise à jour du compte Wave");
|
||||||
Map<String, String> erreur = new HashMap<>();
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
erreur.put("erreur", "Erreur lors de la vérification du statut");
|
.entity(new ErrorResponse("Erreur lors de la mise à jour du compte Wave: " + e.getMessage()))
|
||||||
erreur.put("message", e.getMessage());
|
.build();
|
||||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(erreur).build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie un compte Wave
|
||||||
|
*
|
||||||
|
* @param id ID du compte
|
||||||
|
* @return Compte vérifié
|
||||||
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Path("/webhooks")
|
@Path("/comptes/{id}/verifier")
|
||||||
@Operation(
|
public Response verifierCompteWave(@PathParam("id") UUID id) {
|
||||||
summary = "Recevoir un webhook Wave",
|
|
||||||
description = "Endpoint pour recevoir les notifications webhook de Wave")
|
|
||||||
@APIResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "Webhook reçu et traité",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = MediaType.APPLICATION_JSON,
|
|
||||||
schema = @Schema(implementation = WaveWebhookDTO.class)))
|
|
||||||
@APIResponse(responseCode = "400", description = "Webhook invalide")
|
|
||||||
@APIResponse(responseCode = "401", description = "Signature invalide")
|
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
public Response recevoirWebhook(
|
|
||||||
@Parameter(description = "Payload du webhook", required = true) String payload,
|
|
||||||
@jakarta.ws.rs.HeaderParam("X-Wave-Signature") String signature) {
|
|
||||||
try {
|
try {
|
||||||
// Récupérer les headers
|
CompteWaveDTO result = waveService.verifierCompteWave(id);
|
||||||
Map<String, String> headers = new HashMap<>();
|
return Response.ok(result).build();
|
||||||
if (signature != null) {
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
headers.put("X-Wave-Signature", signature);
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
}
|
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||||
|
.build();
|
||||||
WaveWebhookDTO webhook = waveService.traiterWebhook(payload, signature, headers);
|
|
||||||
return Response.ok(webhook).build();
|
|
||||||
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
LOG.warnf("Signature webhook invalide: %s", e.getMessage());
|
|
||||||
return Response.status(Response.Status.UNAUTHORIZED).build();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.errorf(e, "Erreur lors du traitement du webhook: %s", e.getMessage());
|
LOG.errorf(e, "Erreur lors de la vérification du compte Wave");
|
||||||
Map<String, String> erreur = new HashMap<>();
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
erreur.put("erreur", "Erreur lors du traitement du webhook");
|
.entity(new ErrorResponse("Erreur lors de la vérification du compte Wave: " + e.getMessage()))
|
||||||
erreur.put("message", e.getMessage());
|
.build();
|
||||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(erreur).build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un compte Wave par son ID
|
||||||
|
*
|
||||||
|
* @param id ID du compte
|
||||||
|
* @return Compte Wave
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("/balance")
|
@Path("/comptes/{id}")
|
||||||
@Operation(
|
public Response trouverCompteWaveParId(@PathParam("id") UUID id) {
|
||||||
summary = "Consulter le solde Wave",
|
|
||||||
description = "Récupère le solde disponible du wallet Wave")
|
|
||||||
@APIResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "Solde récupéré avec succès",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = MediaType.APPLICATION_JSON,
|
|
||||||
schema = @Schema(implementation = WaveBalanceDTO.class)))
|
|
||||||
public Response consulterSolde() {
|
|
||||||
try {
|
try {
|
||||||
WaveBalanceDTO balance = waveService.consulterSolde();
|
CompteWaveDTO result = waveService.trouverCompteWaveParId(id);
|
||||||
return Response.ok(balance).build();
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||||
|
.build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.errorf(e, "Erreur lors de la consultation du solde: %s", e.getMessage());
|
LOG.errorf(e, "Erreur lors de la recherche du compte Wave");
|
||||||
Map<String, String> erreur = new HashMap<>();
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
erreur.put("erreur", "Erreur lors de la consultation du solde");
|
.entity(new ErrorResponse("Erreur lors de la recherche du compte Wave: " + e.getMessage()))
|
||||||
erreur.put("message", e.getMessage());
|
.build();
|
||||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(erreur).build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un compte Wave par numéro de téléphone
|
||||||
|
*
|
||||||
|
* @param numeroTelephone Numéro de téléphone
|
||||||
|
* @return Compte Wave ou null
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("/test")
|
@Path("/comptes/telephone/{numeroTelephone}")
|
||||||
@Operation(
|
public Response trouverCompteWaveParTelephone(@PathParam("numeroTelephone") String numeroTelephone) {
|
||||||
summary = "Tester la connexion Wave",
|
try {
|
||||||
description = "Teste la connexion et la configuration de l'API Wave")
|
CompteWaveDTO result = waveService.trouverCompteWaveParTelephone(numeroTelephone);
|
||||||
@APIResponse(responseCode = "200", description = "Test effectué")
|
if (result == null) {
|
||||||
public Response testerConnexion() {
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
Map<String, Object> resultat = waveService.testerConnexion();
|
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||||
return Response.ok(resultat).build();
|
.build();
|
||||||
|
}
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la recherche du compte Wave");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la recherche du compte Wave: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste tous les comptes Wave d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return Liste des comptes Wave
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/comptes/organisation/{organisationId}")
|
||||||
|
public Response listerComptesWaveParOrganisation(@PathParam("organisationId") UUID organisationId) {
|
||||||
|
try {
|
||||||
|
List<CompteWaveDTO> result = waveService.listerComptesWaveParOrganisation(organisationId);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la liste des comptes Wave");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la liste des comptes Wave: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// TRANSACTIONS WAVE
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une nouvelle transaction Wave
|
||||||
|
*
|
||||||
|
* @param transactionWaveDTO DTO de la transaction à créer
|
||||||
|
* @return Transaction créée
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/transactions")
|
||||||
|
public Response creerTransactionWave(@Valid TransactionWaveDTO transactionWaveDTO) {
|
||||||
|
try {
|
||||||
|
TransactionWaveDTO result = waveService.creerTransactionWave(transactionWaveDTO);
|
||||||
|
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la création de la transaction Wave");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la création de la transaction Wave: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour le statut d'une transaction Wave
|
||||||
|
*
|
||||||
|
* @param waveTransactionId Identifiant Wave de la transaction
|
||||||
|
* @param statut Nouveau statut
|
||||||
|
* @return Transaction mise à jour
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("/transactions/{waveTransactionId}/statut")
|
||||||
|
public Response mettreAJourStatutTransaction(
|
||||||
|
@PathParam("waveTransactionId") String waveTransactionId, StatutTransactionWave statut) {
|
||||||
|
try {
|
||||||
|
TransactionWaveDTO result = waveService.mettreAJourStatutTransaction(waveTransactionId, statut);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Transaction Wave non trouvée"))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la mise à jour du statut de la transaction Wave");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(
|
||||||
|
new ErrorResponse(
|
||||||
|
"Erreur lors de la mise à jour du statut de la transaction Wave: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une transaction Wave par son identifiant Wave
|
||||||
|
*
|
||||||
|
* @param waveTransactionId Identifiant Wave
|
||||||
|
* @return Transaction Wave
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/transactions/{waveTransactionId}")
|
||||||
|
public Response trouverTransactionWaveParId(@PathParam("waveTransactionId") String waveTransactionId) {
|
||||||
|
try {
|
||||||
|
TransactionWaveDTO result = waveService.trouverTransactionWaveParId(waveTransactionId);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Transaction Wave non trouvée"))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.errorf(e, "Erreur lors de la recherche de la transaction Wave");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(new ErrorResponse("Erreur lors de la recherche de la transaction Wave: " + e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Classe interne pour les réponses d'erreur */
|
||||||
|
public static class ErrorResponse {
|
||||||
|
public String error;
|
||||||
|
|
||||||
|
public ErrorResponse(String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,174 +1,309 @@
|
|||||||
package dev.lions.unionflow.server.service;
|
package dev.lions.unionflow.server.service;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.paiement.PaiementDTO;
|
||||||
|
import dev.lions.unionflow.server.api.enums.paiement.StatutPaiement;
|
||||||
|
import dev.lions.unionflow.server.entity.Membre;
|
||||||
|
import dev.lions.unionflow.server.entity.Paiement;
|
||||||
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.PaiementRepository;
|
||||||
|
import dev.lions.unionflow.server.service.KeycloakService;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service métier pour la gestion des paiements Mobile Money Intègre Wave Money, Orange Money, et
|
* Service métier pour la gestion des paiements
|
||||||
* Moov Money
|
|
||||||
*
|
*
|
||||||
* @author UnionFlow Team
|
* @author UnionFlow Team
|
||||||
* @version 1.0
|
* @version 3.0
|
||||||
* @since 2025-01-15
|
* @since 2025-01-29
|
||||||
*/
|
*/
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class PaiementService {
|
public class PaiementService {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(PaiementService.class);
|
private static final Logger LOG = Logger.getLogger(PaiementService.class);
|
||||||
|
|
||||||
|
@Inject PaiementRepository paiementRepository;
|
||||||
|
|
||||||
|
@Inject MembreRepository membreRepository;
|
||||||
|
|
||||||
|
@Inject KeycloakService keycloakService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initie un paiement Mobile Money
|
* Crée un nouveau paiement
|
||||||
*
|
*
|
||||||
* @param paymentData données du paiement
|
* @param paiementDTO DTO du paiement à créer
|
||||||
* @return informations du paiement initié
|
* @return DTO du paiement créé
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public Map<String, Object> initiatePayment(@Valid Map<String, Object> paymentData) {
|
public PaiementDTO creerPaiement(PaiementDTO paiementDTO) {
|
||||||
LOG.infof("Initiation d'un paiement");
|
LOG.infof("Création d'un nouveau paiement: %s", paiementDTO.getNumeroReference());
|
||||||
|
|
||||||
try {
|
Paiement paiement = convertToEntity(paiementDTO);
|
||||||
String operateur = (String) paymentData.get("operateur");
|
paiement.setCreePar(keycloakService.getCurrentUserEmail());
|
||||||
BigDecimal montant = new BigDecimal(paymentData.get("montant").toString());
|
|
||||||
String numeroTelephone = (String) paymentData.get("numeroTelephone");
|
|
||||||
String cotisationId = (String) paymentData.get("cotisationId");
|
|
||||||
|
|
||||||
// Générer un ID unique pour le paiement
|
paiementRepository.persist(paiement);
|
||||||
String paymentId = UUID.randomUUID().toString();
|
LOG.infof("Paiement créé avec succès: ID=%s, Référence=%s", paiement.getId(), paiement.getNumeroReference());
|
||||||
String numeroReference = "PAY-" + System.currentTimeMillis();
|
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
return convertToDTO(paiement);
|
||||||
response.put("id", paymentId);
|
|
||||||
response.put("cotisationId", cotisationId);
|
|
||||||
response.put("numeroReference", numeroReference);
|
|
||||||
response.put("montant", montant);
|
|
||||||
response.put("codeDevise", "XOF");
|
|
||||||
response.put("methodePaiement", operateur != null ? operateur.toUpperCase() : "WAVE");
|
|
||||||
response.put("statut", "PENDING");
|
|
||||||
response.put("dateTransaction", LocalDateTime.now().toString());
|
|
||||||
response.put("numeroTransaction", numeroReference);
|
|
||||||
response.put("operateurMobileMoney", operateur != null ? operateur.toUpperCase() : "WAVE");
|
|
||||||
response.put("numeroTelephone", numeroTelephone);
|
|
||||||
response.put("dateCreation", LocalDateTime.now().toString());
|
|
||||||
|
|
||||||
// Métadonnées
|
|
||||||
Map<String, Object> metadonnees = new HashMap<>();
|
|
||||||
metadonnees.put("source", "unionflow_mobile");
|
|
||||||
metadonnees.put("operateur", operateur);
|
|
||||||
metadonnees.put("numero_telephone", numeroTelephone);
|
|
||||||
metadonnees.put("cotisation_id", cotisationId);
|
|
||||||
response.put("metadonnees", metadonnees);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.errorf("Erreur lors de l'initiation du paiement: %s", e.getMessage());
|
|
||||||
throw new RuntimeException("Erreur lors de l'initiation du paiement: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère le statut d'un paiement
|
* Met à jour un paiement existant
|
||||||
*
|
*
|
||||||
* @param paymentId ID du paiement
|
* @param id ID du paiement
|
||||||
* @return statut du paiement
|
* @param paiementDTO DTO avec les modifications
|
||||||
|
* @return DTO du paiement mis à jour
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> getPaymentStatus(@NotNull String paymentId) {
|
@Transactional
|
||||||
LOG.infof("Récupération du statut du paiement: %s", paymentId);
|
public PaiementDTO mettreAJourPaiement(UUID id, PaiementDTO paiementDTO) {
|
||||||
|
LOG.infof("Mise à jour du paiement ID: %s", id);
|
||||||
|
|
||||||
// Simulation du statut
|
Paiement paiement =
|
||||||
Map<String, Object> status = new HashMap<>();
|
paiementRepository
|
||||||
status.put("id", paymentId);
|
.findByIdOptional(id)
|
||||||
status.put("statut", "COMPLETED"); // Simulation d'un paiement réussi
|
.orElseThrow(() -> new NotFoundException("Paiement non trouvé avec l'ID: " + id));
|
||||||
status.put("dateModification", LocalDateTime.now().toString());
|
|
||||||
status.put("message", "Paiement traité avec succès");
|
|
||||||
|
|
||||||
return status;
|
if (!paiement.peutEtreModifie()) {
|
||||||
|
throw new IllegalStateException("Le paiement ne peut plus être modifié (statut finalisé)");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFromDTO(paiement, paiementDTO);
|
||||||
|
paiement.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
paiementRepository.persist(paiement);
|
||||||
|
LOG.infof("Paiement mis à jour avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return convertToDTO(paiement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valide un paiement
|
||||||
|
*
|
||||||
|
* @param id ID du paiement
|
||||||
|
* @return DTO du paiement validé
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public PaiementDTO validerPaiement(UUID id) {
|
||||||
|
LOG.infof("Validation du paiement ID: %s", id);
|
||||||
|
|
||||||
|
Paiement paiement =
|
||||||
|
paiementRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Paiement non trouvé avec l'ID: " + id));
|
||||||
|
|
||||||
|
if (paiement.isValide()) {
|
||||||
|
LOG.warnf("Le paiement ID=%s est déjà validé", id);
|
||||||
|
return convertToDTO(paiement);
|
||||||
|
}
|
||||||
|
|
||||||
|
paiement.setStatutPaiement(StatutPaiement.VALIDE);
|
||||||
|
paiement.setDateValidation(LocalDateTime.now());
|
||||||
|
paiement.setValidateur(keycloakService.getCurrentUserEmail());
|
||||||
|
paiement.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
paiementRepository.persist(paiement);
|
||||||
|
LOG.infof("Paiement validé avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return convertToDTO(paiement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annule un paiement
|
* Annule un paiement
|
||||||
*
|
*
|
||||||
* @param paymentId ID du paiement
|
* @param id ID du paiement
|
||||||
* @param cotisationId ID de la cotisation
|
* @return DTO du paiement annulé
|
||||||
* @return résultat de l'annulation
|
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public Map<String, Object> cancelPayment(
|
public PaiementDTO annulerPaiement(UUID id) {
|
||||||
@NotNull String paymentId, @NotNull String cotisationId) {
|
LOG.infof("Annulation du paiement ID: %s", id);
|
||||||
LOG.infof("Annulation du paiement: %s pour cotisation: %s", paymentId, cotisationId);
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
Paiement paiement =
|
||||||
result.put("id", paymentId);
|
paiementRepository
|
||||||
result.put("cotisationId", cotisationId);
|
.findByIdOptional(id)
|
||||||
result.put("statut", "CANCELLED");
|
.orElseThrow(() -> new NotFoundException("Paiement non trouvé avec l'ID: " + id));
|
||||||
result.put("dateAnnulation", LocalDateTime.now().toString());
|
|
||||||
result.put("message", "Paiement annulé avec succès");
|
|
||||||
|
|
||||||
return result;
|
if (!paiement.peutEtreModifie()) {
|
||||||
|
throw new IllegalStateException("Le paiement ne peut plus être annulé (statut finalisé)");
|
||||||
|
}
|
||||||
|
|
||||||
|
paiement.setStatutPaiement(StatutPaiement.ANNULE);
|
||||||
|
paiement.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
paiementRepository.persist(paiement);
|
||||||
|
LOG.infof("Paiement annulé avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return convertToDTO(paiement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère l'historique des paiements
|
* Trouve un paiement par son ID
|
||||||
*
|
*
|
||||||
* @param filters filtres de recherche
|
* @param id ID du paiement
|
||||||
* @return liste des paiements
|
* @return DTO du paiement
|
||||||
*/
|
*/
|
||||||
public List<Map<String, Object>> getPaymentHistory(Map<String, Object> filters) {
|
public PaiementDTO trouverParId(UUID id) {
|
||||||
LOG.info("Récupération de l'historique des paiements");
|
return paiementRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
// Simulation d'un historique vide pour l'instant
|
.map(this::convertToDTO)
|
||||||
return List.of();
|
.orElseThrow(() -> new NotFoundException("Paiement non trouvé avec l'ID: " + id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vérifie le statut d'un service de paiement
|
* Trouve un paiement par son numéro de référence
|
||||||
*
|
*
|
||||||
* @param serviceType type de service (WAVE, ORANGE_MONEY, MOOV_MONEY)
|
* @param numeroReference Numéro de référence
|
||||||
* @return statut du service
|
* @return DTO du paiement
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> checkServiceStatus(@NotNull String serviceType) {
|
public PaiementDTO trouverParNumeroReference(String numeroReference) {
|
||||||
LOG.infof("Vérification du statut du service: %s", serviceType);
|
return paiementRepository
|
||||||
|
.findByNumeroReference(numeroReference)
|
||||||
Map<String, Object> status = new HashMap<>();
|
.map(this::convertToDTO)
|
||||||
status.put("service", serviceType);
|
.orElseThrow(() -> new NotFoundException("Paiement non trouvé avec la référence: " + numeroReference));
|
||||||
status.put("statut", "OPERATIONAL");
|
|
||||||
status.put("disponible", true);
|
|
||||||
status.put("derniereMiseAJour", LocalDateTime.now().toString());
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère les statistiques de paiement
|
* Liste tous les paiements d'un membre
|
||||||
*
|
*
|
||||||
* @param filters filtres pour les statistiques
|
* @param membreId ID du membre
|
||||||
* @return statistiques des paiements
|
* @return Liste des paiements
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> getPaymentStatistics(Map<String, Object> filters) {
|
public List<PaiementDTO> listerParMembre(UUID membreId) {
|
||||||
LOG.info("Récupération des statistiques de paiement");
|
return paiementRepository.findByMembreId(membreId).stream()
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, Object> stats = new HashMap<>();
|
/**
|
||||||
stats.put("totalPaiements", 0);
|
* Calcule le montant total des paiements validés dans une période
|
||||||
stats.put("montantTotal", BigDecimal.ZERO);
|
*
|
||||||
stats.put("paiementsReussis", 0);
|
* @param dateDebut Date de début
|
||||||
stats.put("paiementsEchoues", 0);
|
* @param dateFin Date de fin
|
||||||
stats.put("paiementsEnAttente", 0);
|
* @return Montant total
|
||||||
stats.put(
|
*/
|
||||||
"operateurs",
|
public BigDecimal calculerMontantTotalValides(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||||
Map.of(
|
return paiementRepository.calculerMontantTotalValides(dateDebut, dateFin);
|
||||||
"WAVE", 0,
|
}
|
||||||
"ORANGE_MONEY", 0,
|
|
||||||
"MOOV_MONEY", 0));
|
|
||||||
|
|
||||||
return stats;
|
// ========================================
|
||||||
|
// MÉTHODES PRIVÉES
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/** Convertit une entité en DTO */
|
||||||
|
private PaiementDTO convertToDTO(Paiement paiement) {
|
||||||
|
if (paiement == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PaiementDTO dto = new PaiementDTO();
|
||||||
|
dto.setId(paiement.getId());
|
||||||
|
dto.setNumeroReference(paiement.getNumeroReference());
|
||||||
|
dto.setMontant(paiement.getMontant());
|
||||||
|
dto.setCodeDevise(paiement.getCodeDevise());
|
||||||
|
dto.setMethodePaiement(paiement.getMethodePaiement());
|
||||||
|
dto.setStatutPaiement(paiement.getStatutPaiement());
|
||||||
|
dto.setDatePaiement(paiement.getDatePaiement());
|
||||||
|
dto.setDateValidation(paiement.getDateValidation());
|
||||||
|
dto.setValidateur(paiement.getValidateur());
|
||||||
|
dto.setReferenceExterne(paiement.getReferenceExterne());
|
||||||
|
dto.setUrlPreuve(paiement.getUrlPreuve());
|
||||||
|
dto.setCommentaire(paiement.getCommentaire());
|
||||||
|
dto.setIpAddress(paiement.getIpAddress());
|
||||||
|
dto.setUserAgent(paiement.getUserAgent());
|
||||||
|
|
||||||
|
if (paiement.getMembre() != null) {
|
||||||
|
dto.setMembreId(paiement.getMembre().getId());
|
||||||
|
}
|
||||||
|
if (paiement.getTransactionWave() != null) {
|
||||||
|
dto.setTransactionWaveId(paiement.getTransactionWave().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setDateCreation(paiement.getDateCreation());
|
||||||
|
dto.setDateModification(paiement.getDateModification());
|
||||||
|
dto.setActif(paiement.getActif());
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convertit un DTO en entité */
|
||||||
|
private Paiement convertToEntity(PaiementDTO dto) {
|
||||||
|
if (dto == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Paiement paiement = new Paiement();
|
||||||
|
paiement.setNumeroReference(dto.getNumeroReference());
|
||||||
|
paiement.setMontant(dto.getMontant());
|
||||||
|
paiement.setCodeDevise(dto.getCodeDevise());
|
||||||
|
paiement.setMethodePaiement(dto.getMethodePaiement());
|
||||||
|
paiement.setStatutPaiement(dto.getStatutPaiement() != null ? dto.getStatutPaiement() : StatutPaiement.EN_ATTENTE);
|
||||||
|
paiement.setDatePaiement(dto.getDatePaiement());
|
||||||
|
paiement.setDateValidation(dto.getDateValidation());
|
||||||
|
paiement.setValidateur(dto.getValidateur());
|
||||||
|
paiement.setReferenceExterne(dto.getReferenceExterne());
|
||||||
|
paiement.setUrlPreuve(dto.getUrlPreuve());
|
||||||
|
paiement.setCommentaire(dto.getCommentaire());
|
||||||
|
paiement.setIpAddress(dto.getIpAddress());
|
||||||
|
paiement.setUserAgent(dto.getUserAgent());
|
||||||
|
|
||||||
|
// Relation Membre
|
||||||
|
if (dto.getMembreId() != null) {
|
||||||
|
Membre membre =
|
||||||
|
membreRepository
|
||||||
|
.findByIdOptional(dto.getMembreId())
|
||||||
|
.orElseThrow(() -> new NotFoundException("Membre non trouvé avec l'ID: " + dto.getMembreId()));
|
||||||
|
paiement.setMembre(membre);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relation TransactionWave sera gérée par WaveService
|
||||||
|
|
||||||
|
return paiement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Met à jour une entité à partir d'un DTO */
|
||||||
|
private void updateFromDTO(Paiement paiement, PaiementDTO dto) {
|
||||||
|
if (dto.getMontant() != null) {
|
||||||
|
paiement.setMontant(dto.getMontant());
|
||||||
|
}
|
||||||
|
if (dto.getCodeDevise() != null) {
|
||||||
|
paiement.setCodeDevise(dto.getCodeDevise());
|
||||||
|
}
|
||||||
|
if (dto.getMethodePaiement() != null) {
|
||||||
|
paiement.setMethodePaiement(dto.getMethodePaiement());
|
||||||
|
}
|
||||||
|
if (dto.getStatutPaiement() != null) {
|
||||||
|
paiement.setStatutPaiement(dto.getStatutPaiement());
|
||||||
|
}
|
||||||
|
if (dto.getDatePaiement() != null) {
|
||||||
|
paiement.setDatePaiement(dto.getDatePaiement());
|
||||||
|
}
|
||||||
|
if (dto.getDateValidation() != null) {
|
||||||
|
paiement.setDateValidation(dto.getDateValidation());
|
||||||
|
}
|
||||||
|
if (dto.getValidateur() != null) {
|
||||||
|
paiement.setValidateur(dto.getValidateur());
|
||||||
|
}
|
||||||
|
if (dto.getReferenceExterne() != null) {
|
||||||
|
paiement.setReferenceExterne(dto.getReferenceExterne());
|
||||||
|
}
|
||||||
|
if (dto.getUrlPreuve() != null) {
|
||||||
|
paiement.setUrlPreuve(dto.getUrlPreuve());
|
||||||
|
}
|
||||||
|
if (dto.getCommentaire() != null) {
|
||||||
|
paiement.setCommentaire(dto.getCommentaire());
|
||||||
|
}
|
||||||
|
if (dto.getIpAddress() != null) {
|
||||||
|
paiement.setIpAddress(dto.getIpAddress());
|
||||||
|
}
|
||||||
|
if (dto.getUserAgent() != null) {
|
||||||
|
paiement.setUserAgent(dto.getUserAgent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,281 +1,393 @@
|
|||||||
package dev.lions.unionflow.server.service;
|
package dev.lions.unionflow.server.service;
|
||||||
|
|
||||||
import dev.lions.unionflow.server.api.dto.paiement.WaveBalanceDTO;
|
import dev.lions.unionflow.server.api.dto.wave.CompteWaveDTO;
|
||||||
import dev.lions.unionflow.server.api.dto.paiement.WaveCheckoutSessionDTO;
|
import dev.lions.unionflow.server.api.dto.wave.TransactionWaveDTO;
|
||||||
import dev.lions.unionflow.server.api.dto.paiement.WaveWebhookDTO;
|
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
|
||||||
import dev.lions.unionflow.server.api.enums.paiement.StatutSession;
|
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||||
import dev.lions.unionflow.server.api.enums.paiement.TypeEvenement;
|
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
|
||||||
|
import dev.lions.unionflow.server.entity.CompteWave;
|
||||||
|
import dev.lions.unionflow.server.entity.Organisation;
|
||||||
|
import dev.lions.unionflow.server.entity.Membre;
|
||||||
|
import dev.lions.unionflow.server.entity.TransactionWave;
|
||||||
|
import dev.lions.unionflow.server.repository.CompteWaveRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.TransactionWaveRepository;
|
||||||
|
import dev.lions.unionflow.server.service.KeycloakService;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashMap;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import java.util.stream.Collectors;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service pour l'intégration Wave Money
|
* Service métier pour l'intégration Wave Mobile Money
|
||||||
* Gère les sessions de paiement, les webhooks et la consultation du solde
|
|
||||||
*
|
*
|
||||||
* @author UnionFlow Team
|
* @author UnionFlow Team
|
||||||
* @version 1.0
|
* @version 3.0
|
||||||
* @since 2025-01-17
|
* @since 2025-01-29
|
||||||
*/
|
*/
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class WaveService {
|
public class WaveService {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(WaveService.class);
|
private static final Logger LOG = Logger.getLogger(WaveService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject CompteWaveRepository compteWaveRepository;
|
||||||
@ConfigProperty(name = "wave.api.key")
|
|
||||||
Optional<String> waveApiKey;
|
|
||||||
|
|
||||||
@Inject
|
@Inject TransactionWaveRepository transactionWaveRepository;
|
||||||
@ConfigProperty(name = "wave.api.secret")
|
|
||||||
Optional<String> waveApiSecret;
|
|
||||||
|
|
||||||
@Inject
|
@Inject OrganisationRepository organisationRepository;
|
||||||
@ConfigProperty(name = "wave.api.base.url", defaultValue = "https://api.wave.com/v1")
|
|
||||||
String waveApiBaseUrl;
|
|
||||||
|
|
||||||
@Inject
|
@Inject MembreRepository membreRepository;
|
||||||
@ConfigProperty(name = "wave.environment", defaultValue = "sandbox")
|
|
||||||
String waveEnvironment;
|
|
||||||
|
|
||||||
@Inject
|
@Inject KeycloakService keycloakService;
|
||||||
@ConfigProperty(name = "wave.webhook.secret")
|
|
||||||
Optional<String> waveWebhookSecret;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crée une session de paiement Wave Checkout
|
* Crée un nouveau compte Wave
|
||||||
|
*
|
||||||
|
* @param compteWaveDTO DTO du compte à créer
|
||||||
|
* @return DTO du compte créé
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public CompteWaveDTO creerCompteWave(CompteWaveDTO compteWaveDTO) {
|
||||||
|
LOG.infof("Création d'un nouveau compte Wave: %s", compteWaveDTO.getNumeroTelephone());
|
||||||
|
|
||||||
|
// Vérifier l'unicité du numéro de téléphone
|
||||||
|
if (compteWaveRepository.findByNumeroTelephone(compteWaveDTO.getNumeroTelephone()).isPresent()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Un compte Wave existe déjà pour ce numéro: " + compteWaveDTO.getNumeroTelephone());
|
||||||
|
}
|
||||||
|
|
||||||
|
CompteWave compteWave = convertToEntity(compteWaveDTO);
|
||||||
|
compteWave.setCreePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
compteWaveRepository.persist(compteWave);
|
||||||
|
LOG.infof("Compte Wave créé avec succès: ID=%s, Téléphone=%s", compteWave.getId(), compteWave.getNumeroTelephone());
|
||||||
|
|
||||||
|
return convertToDTO(compteWave);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour un compte Wave
|
||||||
|
*
|
||||||
|
* @param id ID du compte
|
||||||
|
* @param compteWaveDTO DTO avec les modifications
|
||||||
|
* @return DTO du compte mis à jour
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public CompteWaveDTO mettreAJourCompteWave(UUID id, CompteWaveDTO compteWaveDTO) {
|
||||||
|
LOG.infof("Mise à jour du compte Wave ID: %s", id);
|
||||||
|
|
||||||
|
CompteWave compteWave =
|
||||||
|
compteWaveRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Compte Wave non trouvé avec l'ID: " + id));
|
||||||
|
|
||||||
|
updateFromDTO(compteWave, compteWaveDTO);
|
||||||
|
compteWave.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
compteWaveRepository.persist(compteWave);
|
||||||
|
LOG.infof("Compte Wave mis à jour avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return convertToDTO(compteWave);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie un compte Wave
|
||||||
|
*
|
||||||
|
* @param id ID du compte
|
||||||
|
* @return DTO du compte vérifié
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public CompteWaveDTO verifierCompteWave(UUID id) {
|
||||||
|
LOG.infof("Vérification du compte Wave ID: %s", id);
|
||||||
|
|
||||||
|
CompteWave compteWave =
|
||||||
|
compteWaveRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Compte Wave non trouvé avec l'ID: " + id));
|
||||||
|
|
||||||
|
compteWave.setStatutCompte(StatutCompteWave.VERIFIE);
|
||||||
|
compteWave.setDateDerniereVerification(LocalDateTime.now());
|
||||||
|
compteWave.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
compteWaveRepository.persist(compteWave);
|
||||||
|
LOG.infof("Compte Wave vérifié avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return convertToDTO(compteWave);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un compte Wave par son ID
|
||||||
|
*
|
||||||
|
* @param id ID du compte
|
||||||
|
* @return DTO du compte
|
||||||
|
*/
|
||||||
|
public CompteWaveDTO trouverCompteWaveParId(UUID id) {
|
||||||
|
return compteWaveRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Compte Wave non trouvé avec l'ID: " + id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un compte Wave par numéro de téléphone
|
||||||
|
*
|
||||||
|
* @param numeroTelephone Numéro de téléphone
|
||||||
|
* @return DTO du compte ou null
|
||||||
|
*/
|
||||||
|
public CompteWaveDTO trouverCompteWaveParTelephone(String numeroTelephone) {
|
||||||
|
return compteWaveRepository
|
||||||
|
.findByNumeroTelephone(numeroTelephone)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste tous les comptes Wave d'une organisation
|
||||||
*
|
*
|
||||||
* @param montant Montant à payer
|
|
||||||
* @param devise Devise (XOF par défaut)
|
|
||||||
* @param successUrl URL de redirection en cas de succès
|
|
||||||
* @param errorUrl URL de redirection en cas d'erreur
|
|
||||||
* @param referenceUnionFlow Référence interne UnionFlow
|
|
||||||
* @param description Description du paiement
|
|
||||||
* @param organisationId ID de l'organisation
|
* @param organisationId ID de l'organisation
|
||||||
* @param membreId ID du membre
|
* @return Liste des comptes Wave
|
||||||
* @return Session de paiement créée
|
*/
|
||||||
|
public List<CompteWaveDTO> listerComptesWaveParOrganisation(UUID organisationId) {
|
||||||
|
return compteWaveRepository.findByOrganisationId(organisationId).stream()
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une nouvelle transaction Wave
|
||||||
|
*
|
||||||
|
* @param transactionWaveDTO DTO de la transaction à créer
|
||||||
|
* @return DTO de la transaction créée
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public WaveCheckoutSessionDTO creerSessionPaiement(
|
public TransactionWaveDTO creerTransactionWave(TransactionWaveDTO transactionWaveDTO) {
|
||||||
BigDecimal montant,
|
LOG.infof("Création d'une nouvelle transaction Wave: %s", transactionWaveDTO.getWaveTransactionId());
|
||||||
String devise,
|
|
||||||
String successUrl,
|
TransactionWave transactionWave = convertToEntity(transactionWaveDTO);
|
||||||
String errorUrl,
|
transactionWave.setCreePar(keycloakService.getCurrentUserEmail());
|
||||||
String referenceUnionFlow,
|
|
||||||
String description,
|
transactionWaveRepository.persist(transactionWave);
|
||||||
UUID organisationId,
|
|
||||||
UUID membreId) {
|
|
||||||
LOG.infof(
|
LOG.infof(
|
||||||
"Création d'une session de paiement Wave: montant=%s, devise=%s, ref=%s",
|
"Transaction Wave créée avec succès: ID=%s, WaveTransactionId=%s",
|
||||||
montant, devise, referenceUnionFlow);
|
transactionWave.getId(), transactionWave.getWaveTransactionId());
|
||||||
|
|
||||||
try {
|
return convertToDTO(transactionWave);
|
||||||
// TODO: Appel réel à l'API Wave Checkout
|
|
||||||
// Pour l'instant, simulation de la création de session
|
|
||||||
String waveSessionId = "wave_session_" + UUID.randomUUID().toString().replace("-", "");
|
|
||||||
String waveUrl = buildWaveCheckoutUrl(waveSessionId);
|
|
||||||
|
|
||||||
WaveCheckoutSessionDTO session = new WaveCheckoutSessionDTO();
|
|
||||||
session.setId(UUID.randomUUID());
|
|
||||||
session.setWaveSessionId(waveSessionId);
|
|
||||||
session.setWaveUrl(waveUrl);
|
|
||||||
session.setMontant(montant);
|
|
||||||
session.setDevise(devise != null ? devise : "XOF");
|
|
||||||
session.setSuccessUrl(successUrl);
|
|
||||||
session.setErrorUrl(errorUrl);
|
|
||||||
session.setReferenceUnionFlow(referenceUnionFlow);
|
|
||||||
session.setDescription(description);
|
|
||||||
session.setOrganisationId(organisationId);
|
|
||||||
session.setMembreId(membreId);
|
|
||||||
session.setStatut(StatutSession.PENDING);
|
|
||||||
session.setDateCreation(LocalDateTime.now());
|
|
||||||
session.setDateExpiration(LocalDateTime.now().plusHours(24)); // Expire dans 24h
|
|
||||||
|
|
||||||
LOG.infof("Session Wave créée: %s", waveSessionId);
|
|
||||||
return session;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.errorf(e, "Erreur lors de la création de la session Wave: %s", e.getMessage());
|
|
||||||
throw new RuntimeException("Erreur lors de la création de la session Wave", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vérifie le statut d'une session de paiement
|
* Met à jour le statut d'une transaction Wave
|
||||||
*
|
*
|
||||||
* @param waveSessionId ID de la session Wave
|
* @param waveTransactionId Identifiant Wave de la transaction
|
||||||
* @return Statut de la session
|
* @param nouveauStatut Nouveau statut
|
||||||
*/
|
* @return DTO de la transaction mise à jour
|
||||||
public WaveCheckoutSessionDTO verifierStatutSession(String waveSessionId) {
|
|
||||||
LOG.infof("Vérification du statut de la session Wave: %s", waveSessionId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// TODO: Appel réel à l'API Wave pour vérifier le statut
|
|
||||||
// Pour l'instant, simulation
|
|
||||||
WaveCheckoutSessionDTO session = new WaveCheckoutSessionDTO();
|
|
||||||
session.setWaveSessionId(waveSessionId);
|
|
||||||
session.setStatut(StatutSession.PENDING);
|
|
||||||
return session;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.errorf(e, "Erreur lors de la vérification du statut: %s", e.getMessage());
|
|
||||||
throw new RuntimeException("Erreur lors de la vérification du statut", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Traite un webhook reçu de Wave
|
|
||||||
*
|
|
||||||
* @param payload Payload JSON du webhook
|
|
||||||
* @param signature Signature Wave pour vérification
|
|
||||||
* @param headers Headers HTTP
|
|
||||||
* @return Webhook traité
|
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public WaveWebhookDTO traiterWebhook(String payload, String signature, Map<String, String> headers) {
|
public TransactionWaveDTO mettreAJourStatutTransaction(
|
||||||
LOG.info("Traitement d'un webhook Wave");
|
String waveTransactionId, StatutTransactionWave nouveauStatut) {
|
||||||
|
LOG.infof("Mise à jour du statut de la transaction Wave: %s -> %s", waveTransactionId, nouveauStatut);
|
||||||
|
|
||||||
try {
|
TransactionWave transactionWave =
|
||||||
// Vérifier la signature
|
transactionWaveRepository
|
||||||
if (!verifierSignatureWebhook(payload, signature)) {
|
.findByWaveTransactionId(waveTransactionId)
|
||||||
LOG.warn("Signature webhook invalide");
|
.orElseThrow(
|
||||||
throw new SecurityException("Signature webhook invalide");
|
() ->
|
||||||
}
|
new NotFoundException(
|
||||||
|
"Transaction Wave non trouvée avec l'ID: " + waveTransactionId));
|
||||||
|
|
||||||
// Parser le payload
|
transactionWave.setStatutTransaction(nouveauStatut);
|
||||||
// TODO: Parser réellement le JSON du webhook
|
transactionWave.setDateDerniereTentative(LocalDateTime.now());
|
||||||
String webhookId = "webhook_" + UUID.randomUUID().toString();
|
transactionWave.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
TypeEvenement typeEvenement = TypeEvenement.CHECKOUT_COMPLETE; // À déterminer depuis le payload
|
|
||||||
|
|
||||||
WaveWebhookDTO webhook = new WaveWebhookDTO();
|
transactionWaveRepository.persist(transactionWave);
|
||||||
webhook.setId(UUID.randomUUID());
|
LOG.infof("Statut de la transaction Wave mis à jour: %s", waveTransactionId);
|
||||||
webhook.setWebhookId(webhookId);
|
|
||||||
webhook.setTypeEvenement(typeEvenement);
|
|
||||||
webhook.setCodeEvenement(typeEvenement.getCodeWave());
|
|
||||||
webhook.setPayloadJson(payload);
|
|
||||||
webhook.setSignatureWave(signature);
|
|
||||||
webhook.setDateReception(LocalDateTime.now());
|
|
||||||
webhook.setDateTraitement(LocalDateTime.now());
|
|
||||||
|
|
||||||
// Extraire les informations du payload
|
return convertToDTO(transactionWave);
|
||||||
// TODO: Extraire réellement les données du JSON
|
|
||||||
// webhook.setSessionCheckoutId(...);
|
|
||||||
// webhook.setTransactionWaveId(...);
|
|
||||||
// webhook.setMontantTransaction(...);
|
|
||||||
|
|
||||||
LOG.infof("Webhook traité: %s", webhookId);
|
|
||||||
return webhook;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.errorf(e, "Erreur lors du traitement du webhook: %s", e.getMessage());
|
|
||||||
throw new RuntimeException("Erreur lors du traitement du webhook", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consulte le solde du wallet Wave
|
* Trouve une transaction Wave par son identifiant Wave
|
||||||
*
|
*
|
||||||
* @return Solde du wallet
|
* @param waveTransactionId Identifiant Wave
|
||||||
|
* @return DTO de la transaction
|
||||||
*/
|
*/
|
||||||
public WaveBalanceDTO consulterSolde() {
|
public TransactionWaveDTO trouverTransactionWaveParId(String waveTransactionId) {
|
||||||
LOG.info("Consultation du solde Wave");
|
return transactionWaveRepository
|
||||||
|
.findByWaveTransactionId(waveTransactionId)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new NotFoundException("Transaction Wave non trouvée avec l'ID: " + waveTransactionId));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
// ========================================
|
||||||
// TODO: Appel réel à l'API Wave Balance
|
// MÉTHODES PRIVÉES
|
||||||
// Pour l'instant, simulation
|
// ========================================
|
||||||
WaveBalanceDTO balance = new WaveBalanceDTO();
|
|
||||||
balance.setNumeroWallet("wave_wallet_001");
|
|
||||||
balance.setSoldeDisponible(BigDecimal.ZERO);
|
|
||||||
balance.setSoldeEnAttente(BigDecimal.ZERO);
|
|
||||||
balance.setSoldeTotal(BigDecimal.ZERO);
|
|
||||||
balance.setDevise("XOF");
|
|
||||||
balance.setStatutWallet("ACTIVE");
|
|
||||||
balance.setDateDerniereMiseAJour(LocalDateTime.now());
|
|
||||||
balance.setDateDerniereSynchronisation(LocalDateTime.now());
|
|
||||||
|
|
||||||
return balance;
|
/** Convertit une entité CompteWave en DTO */
|
||||||
|
private CompteWaveDTO convertToDTO(CompteWave compteWave) {
|
||||||
|
if (compteWave == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
CompteWaveDTO dto = new CompteWaveDTO();
|
||||||
LOG.errorf(e, "Erreur lors de la consultation du solde: %s", e.getMessage());
|
dto.setId(compteWave.getId());
|
||||||
throw new RuntimeException("Erreur lors de la consultation du solde", e);
|
dto.setNumeroTelephone(compteWave.getNumeroTelephone());
|
||||||
|
dto.setStatutCompte(compteWave.getStatutCompte());
|
||||||
|
dto.setWaveAccountId(compteWave.getWaveAccountId());
|
||||||
|
dto.setEnvironnement(compteWave.getEnvironnement());
|
||||||
|
dto.setDateDerniereVerification(compteWave.getDateDerniereVerification());
|
||||||
|
dto.setCommentaire(compteWave.getCommentaire());
|
||||||
|
|
||||||
|
if (compteWave.getOrganisation() != null) {
|
||||||
|
dto.setOrganisationId(compteWave.getOrganisation().getId());
|
||||||
|
}
|
||||||
|
if (compteWave.getMembre() != null) {
|
||||||
|
dto.setMembreId(compteWave.getMembre().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setDateCreation(compteWave.getDateCreation());
|
||||||
|
dto.setDateModification(compteWave.getDateModification());
|
||||||
|
dto.setActif(compteWave.getActif());
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convertit un DTO en entité CompteWave */
|
||||||
|
private CompteWave convertToEntity(CompteWaveDTO dto) {
|
||||||
|
if (dto == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompteWave compteWave = new CompteWave();
|
||||||
|
compteWave.setNumeroTelephone(dto.getNumeroTelephone());
|
||||||
|
compteWave.setStatutCompte(dto.getStatutCompte() != null ? dto.getStatutCompte() : StatutCompteWave.NON_VERIFIE);
|
||||||
|
compteWave.setWaveAccountId(dto.getWaveAccountId());
|
||||||
|
compteWave.setEnvironnement(dto.getEnvironnement() != null ? dto.getEnvironnement() : "SANDBOX");
|
||||||
|
compteWave.setDateDerniereVerification(dto.getDateDerniereVerification());
|
||||||
|
compteWave.setCommentaire(dto.getCommentaire());
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
if (dto.getOrganisationId() != null) {
|
||||||
|
Organisation org =
|
||||||
|
organisationRepository
|
||||||
|
.findByIdOptional(dto.getOrganisationId())
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new NotFoundException(
|
||||||
|
"Organisation non trouvée avec l'ID: " + dto.getOrganisationId()));
|
||||||
|
compteWave.setOrganisation(org);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.getMembreId() != null) {
|
||||||
|
Membre membre =
|
||||||
|
membreRepository
|
||||||
|
.findByIdOptional(dto.getMembreId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new NotFoundException("Membre non trouvé avec l'ID: " + dto.getMembreId()));
|
||||||
|
compteWave.setMembre(membre);
|
||||||
|
}
|
||||||
|
|
||||||
|
return compteWave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Met à jour une entité CompteWave à partir d'un DTO */
|
||||||
|
private void updateFromDTO(CompteWave compteWave, CompteWaveDTO dto) {
|
||||||
|
if (dto.getStatutCompte() != null) {
|
||||||
|
compteWave.setStatutCompte(dto.getStatutCompte());
|
||||||
|
}
|
||||||
|
if (dto.getWaveAccountId() != null) {
|
||||||
|
compteWave.setWaveAccountId(dto.getWaveAccountId());
|
||||||
|
}
|
||||||
|
if (dto.getEnvironnement() != null) {
|
||||||
|
compteWave.setEnvironnement(dto.getEnvironnement());
|
||||||
|
}
|
||||||
|
if (dto.getDateDerniereVerification() != null) {
|
||||||
|
compteWave.setDateDerniereVerification(dto.getDateDerniereVerification());
|
||||||
|
}
|
||||||
|
if (dto.getCommentaire() != null) {
|
||||||
|
compteWave.setCommentaire(dto.getCommentaire());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Convertit une entité TransactionWave en DTO */
|
||||||
* Vérifie si Wave est configuré et actif
|
private TransactionWaveDTO convertToDTO(TransactionWave transactionWave) {
|
||||||
*
|
if (transactionWave == null) {
|
||||||
* @return true si Wave est configuré
|
return null;
|
||||||
*/
|
|
||||||
public boolean estConfigure() {
|
|
||||||
return waveApiKey.isPresent() && !waveApiKey.get().isEmpty()
|
|
||||||
&& waveApiSecret.isPresent() && !waveApiSecret.get().isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
TransactionWaveDTO dto = new TransactionWaveDTO();
|
||||||
* Teste la connexion à l'API Wave
|
dto.setId(transactionWave.getId());
|
||||||
*
|
dto.setWaveTransactionId(transactionWave.getWaveTransactionId());
|
||||||
* @return Résultat du test
|
dto.setWaveRequestId(transactionWave.getWaveRequestId());
|
||||||
*/
|
dto.setWaveReference(transactionWave.getWaveReference());
|
||||||
public Map<String, Object> testerConnexion() {
|
dto.setTypeTransaction(transactionWave.getTypeTransaction());
|
||||||
LOG.info("Test de connexion à l'API Wave");
|
dto.setStatutTransaction(transactionWave.getStatutTransaction());
|
||||||
|
dto.setMontant(transactionWave.getMontant());
|
||||||
|
dto.setFrais(transactionWave.getFrais());
|
||||||
|
dto.setMontantNet(transactionWave.getMontantNet());
|
||||||
|
dto.setCodeDevise(transactionWave.getCodeDevise());
|
||||||
|
dto.setTelephonePayeur(transactionWave.getTelephonePayeur());
|
||||||
|
dto.setTelephoneBeneficiaire(transactionWave.getTelephoneBeneficiaire());
|
||||||
|
dto.setMetadonnees(transactionWave.getMetadonnees());
|
||||||
|
dto.setNombreTentatives(transactionWave.getNombreTentatives());
|
||||||
|
dto.setDateDerniereTentative(transactionWave.getDateDerniereTentative());
|
||||||
|
dto.setMessageErreur(transactionWave.getMessageErreur());
|
||||||
|
|
||||||
Map<String, Object> resultat = new HashMap<>();
|
if (transactionWave.getCompteWave() != null) {
|
||||||
resultat.put("configure", estConfigure());
|
dto.setCompteWaveId(transactionWave.getCompteWave().getId());
|
||||||
resultat.put("environment", waveEnvironment);
|
|
||||||
resultat.put("baseUrl", waveApiBaseUrl);
|
|
||||||
resultat.put("timestamp", LocalDateTime.now().toString());
|
|
||||||
|
|
||||||
if (!estConfigure()) {
|
|
||||||
resultat.put("statut", "ERREUR");
|
|
||||||
resultat.put("message", "Wave n'est pas configuré (clés API manquantes)");
|
|
||||||
return resultat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
dto.setDateCreation(transactionWave.getDateCreation());
|
||||||
// TODO: Faire un appel réel à l'API Wave pour tester
|
dto.setDateModification(transactionWave.getDateModification());
|
||||||
resultat.put("statut", "OK");
|
dto.setActif(transactionWave.getActif());
|
||||||
resultat.put("message", "Connexion réussie (simulation)");
|
|
||||||
return resultat;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
return dto;
|
||||||
LOG.errorf(e, "Erreur lors du test de connexion: %s", e.getMessage());
|
|
||||||
resultat.put("statut", "ERREUR");
|
|
||||||
resultat.put("message", "Erreur: " + e.getMessage());
|
|
||||||
return resultat;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Méthodes privées
|
/** Convertit un DTO en entité TransactionWave */
|
||||||
|
private TransactionWave convertToEntity(TransactionWaveDTO dto) {
|
||||||
private String buildWaveCheckoutUrl(String sessionId) {
|
if (dto == null) {
|
||||||
if ("sandbox".equals(waveEnvironment)) {
|
return null;
|
||||||
return "https://checkout-sandbox.wave.com/checkout/" + sessionId;
|
|
||||||
} else {
|
|
||||||
return "https://checkout.wave.com/checkout/" + sessionId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifierSignatureWebhook(String payload, String signature) {
|
TransactionWave transactionWave = new TransactionWave();
|
||||||
if (signature == null || signature.isEmpty()) {
|
transactionWave.setWaveTransactionId(dto.getWaveTransactionId());
|
||||||
return false;
|
transactionWave.setWaveRequestId(dto.getWaveRequestId());
|
||||||
|
transactionWave.setWaveReference(dto.getWaveReference());
|
||||||
|
transactionWave.setTypeTransaction(dto.getTypeTransaction());
|
||||||
|
transactionWave.setStatutTransaction(
|
||||||
|
dto.getStatutTransaction() != null
|
||||||
|
? dto.getStatutTransaction()
|
||||||
|
: StatutTransactionWave.INITIALISE);
|
||||||
|
transactionWave.setMontant(dto.getMontant());
|
||||||
|
transactionWave.setFrais(dto.getFrais());
|
||||||
|
transactionWave.setMontantNet(dto.getMontantNet());
|
||||||
|
transactionWave.setCodeDevise(dto.getCodeDevise() != null ? dto.getCodeDevise() : "XOF");
|
||||||
|
transactionWave.setTelephonePayeur(dto.getTelephonePayeur());
|
||||||
|
transactionWave.setTelephoneBeneficiaire(dto.getTelephoneBeneficiaire());
|
||||||
|
transactionWave.setMetadonnees(dto.getMetadonnees());
|
||||||
|
transactionWave.setNombreTentatives(dto.getNombreTentatives() != null ? dto.getNombreTentatives() : 0);
|
||||||
|
transactionWave.setDateDerniereTentative(dto.getDateDerniereTentative());
|
||||||
|
transactionWave.setMessageErreur(dto.getMessageErreur());
|
||||||
|
|
||||||
|
// Relation CompteWave
|
||||||
|
if (dto.getCompteWaveId() != null) {
|
||||||
|
CompteWave compteWave =
|
||||||
|
compteWaveRepository
|
||||||
|
.findByIdOptional(dto.getCompteWaveId())
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new NotFoundException(
|
||||||
|
"Compte Wave non trouvé avec l'ID: " + dto.getCompteWaveId()));
|
||||||
|
transactionWave.setCompteWave(compteWave);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!waveWebhookSecret.isPresent() || waveWebhookSecret.get().isEmpty()) {
|
return transactionWave;
|
||||||
LOG.warn("Secret webhook non configuré, impossible de vérifier la signature");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implémenter la vérification réelle de la signature HMAC SHA256
|
|
||||||
// La signature Wave est généralement au format: sha256=<hash>
|
|
||||||
return true; // Pour l'instant, on accepte toutes les signatures
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user